Skip to content
Latchkey

Maven settings.xml Server Auth in CI - Inject Credentials Safely

Maven needs a <server> entry to authenticate to a private repository, but committing credentials is unsafe. The reliable CI pattern is to generate settings.xml from secrets at build time and reference it with -s.

What this error means

Private-repo resolution or deploy fails with 401/Unauthorized, or builds work locally (where your ~/.m2/settings.xml has credentials) but fail in CI where that file does not exist.

mvn output
[ERROR] Could not transfer artifact com.example:lib:jar:1.0.0 from/to internal:
Not authorized, ReasonPhrase: Unauthorized.
# locally it worked because ~/.m2/settings.xml had credentials; CI has none

Common causes

CI has no settings.xml with credentials

A developer machine has a populated ~/.m2/settings.xml; the ephemeral CI runner does not, so authenticated requests go out with no credentials.

Secrets not interpolated into the file

A committed settings.xml with placeholder values, or one that references env vars Maven cannot read, leaves the request unauthenticated.

How to fix it

Use the env-var form so secrets stay out of the file

Maven expands ${env.NAME} in settings.xml, so the file can be committed while the secret comes from the CI environment.

.mvn/settings.xml
<settings>
  <servers>
    <server>
      <id>internal</id>
      <username>${env.MVN_USER}</username>
      <password>${env.MVN_TOKEN}</password>
    </server>
  </servers>
</settings>

Pass the file and secrets in the job

Inject the secrets as environment variables and point Maven at the settings file.

.github/workflows/ci.yml
- env:
    MVN_USER: ${{ secrets.MVN_USER }}
    MVN_TOKEN: ${{ secrets.MVN_TOKEN }}
  run: mvn -B -s .mvn/settings.xml verify

How to prevent it

  • Commit a settings.xml that uses ${env.*} placeholders, supply the real values from CI secrets, and never bake tokens into the repo.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →