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.
[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 noneCommon 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.
<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.
- env:
MVN_USER: ${{ secrets.MVN_USER }}
MVN_TOKEN: ${{ secrets.MVN_TOKEN }}
run: mvn -B -s .mvn/settings.xml verifyHow 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.