"Public packages got auto-updated with Dependabot. But the internal npm registry, Artifactory, and ECR private images don't get updated" — a wall you always hit in enterprise. The cause decomposes into two. Does authentication (credentials) not pass, or is there no network reachability (network)? These two have completely different fixes.
This article comprehensively covers private-registry support at the implementation level, as a topic of the Dependabot production-operations guide. From per-type authentication to OIDC and self-hosted runners, it explains with settings usable as-is.
Rules for this article: the
registriestypes, authentication fields, and OIDC support are based on the GitHub official documentation (as of June 2026). Never hardcode credentials in the repository; pass them with Dependabot secrets. Always confirm the latest in the official private-registry configuration before production.
0. The two walls: authentication and reachability
| Wall | Error code (official) | How to clear it |
|---|---|---|
| Authentication | private_source_authentication_failure | The registries block + Dependabot secrets |
| Authentication (certificate) | private_source_certificate_failure | Make the internal-CA / self-signed cert trusted (in practice, a self-hosted runner where you can install the CA) |
| Reachability | private_source_not_reachable | A self-hosted runner (place the runner on the private network) |
| Reachability (timeout) | private_source_timed_out | Check the network route and proxy allowlist. For a private network, a self-hosted runner |
Triage by symptom first: the error code in the registry console or the Dependabot job log identifies which wall you're on.
authentication/certificateis the "authentication wall,"not_reachable/timed_outis the "reachability wall." For the full error-code taxonomy, see the troubleshooting guide.
A cloud managed registry (CodeArtifact, Artifact Registry, Artifactory SaaS) is a public endpoint + authentication, so only the "authentication wall." A registry unreachable from the internet, like inside a VPC, also needs to clear the "reachability wall." First discern which you have.
1. The basics of the registries block
Define registries at the top level of dependabot.yml and link it from each updates job.
version: 2
registries:
my-npm:
type: npm-registry
url: https://npm.example.com
token: ${{secrets.DEPENDABOT_NPM_TOKEN}} # ← Dependabot シークレットを参照
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
registries:
- my-npm # このジョブで使うレジストリ
Most important: what
${{secrets.NAME}}references is the Dependabot-dedicated secret store. The location is Settings → Secrets and variables → Dependabot (repository or organization). It's separate from regular Actions secrets, and Dependabot jobs can't see Actions secrets. This is a design to prevent "a compromised dependency stealing secrets via CI." Secrets are encrypted before reaching GitHub and stay encrypted until the moment Dependabot uses them.
2. Authentication reference by type
The usable authentication fields are fixed per type (official). Here's a table of the commonly used ones.
type | Main authentication fields | replaces-base | Note |
|---|---|---|---|
npm-registry | username/password or token | ✓ | npm/yarn/pnpm |
docker-registry | username/password, or static AWS auth for ECR | ✓ | Container images |
maven-repository | username/password, or OIDC (tenant-id/client-id) | ✓ | Maven/Gradle |
python-index | username/password, token, or OIDC | ✓ | pip/uv/poetry |
nuget-feed | username/password, token, or OIDC | ✗ | .NET |
rubygems-server | username/password or token | ✓ | Bundler |
composer-repository | username/password | — | PHP (GitHub PAT OK) |
cargo-registry | token (optionally registry) | — | Rust |
terraform-registry | token | — | Terraform |
git | username/password | — | Git-sourced dependencies |
helm-registry | username/password | — | HTTP Basic auth only, no OCI |
hex-organization | organization/key | — | Elixir |
hex-repository | repo/url/auth-key (optionally public-key-fingerprint) | — | Elixir self-hosted repo |
goproxy-server | username/password | — | Go module proxy |
pub-repository | url/token | — | Dart/Flutter |
replaces-base: truespecifies "use this private registry as the default instead of the public registry (e.g. registry.npmjs.org)." Use it when you want an internal mirror to be the sole fetch source.
3. GitHub Packages is "special" (no registries needed)
GitHub Packages / GitHub Container Registry (ghcr.io) don't need a registries entry written.
Official: "Dependabot can auto-authenticate with
GITHUB_TOKEN. This uses the same 'Manage Actions access' grant that GitHub Actions workflows use. No PAT or registry entry independabot.ymlis needed."
So internal packages placed in the same organization's GitHub Packages become update targets with zero additional configuration. Access is managed by the repository's "Manage Actions access." This is one of the biggest benefits of leaning into the GitHub ecosystem.
4. "Don't place static tokens" with OIDC
Static tokens/passwords come with the hassle of revocation management, leakage risk, and inventory. With an OIDC (OpenID Connect)-capable registry, you can authenticate with short-lived federation tokens and design it so you store no long-term secrets.
The OIDC-capable ones the official docs list are as follows.
- AWS CodeArtifact
- Azure DevOps Artifacts
- Cloudsmith
- Google Cloud Artifact Registry
- JFrog Artifactory
The form using tenant-id / client-id in maven-repository / python-index / nuget-feed, etc., applies. For both security and cost efficiency (reduced key-management operational load), make OIDC the first choice where possible. It's the same philosophy as keyless CI/CD.
5. Cloud-by-cloud examples
5.1 AWS CodeArtifact (npm / pip)
version: 2
registries:
codeartifact-npm:
type: npm-registry
url: https://my-domain-111122223333.d.codeartifact.ap-northeast-1.amazonaws.com/npm/my-repo/
token: ${{secrets.DEPENDABOT_CODEARTIFACT_TOKEN}}
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
registries: ["codeartifact-npm"]
CodeArtifact also supports OIDC, so where possible lean to OIDC federation rather than a static token.
5.2 Amazon ECR (Docker, static AWS auth)
registries:
ecr:
type: docker-registry
url: 111122223333.dkr.ecr.ap-northeast-1.amazonaws.com
username: ${{secrets.DEPENDABOT_AWS_ACCESS_KEY_ID}}
password: ${{secrets.DEPENDABOT_AWS_SECRET_ACCESS_KEY}}
docker-registry can use static AWS auth for pulling from ECR. Scope the IAM to a read-only least privilege. The pull actions can be limited to the target repository ARN, and no write permission is needed.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DependabotEcrGetToken",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "DependabotEcrPullReadOnly",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:DescribeImages",
"ecr:ListImages"
],
"Resource": "arn:aws:ecr:ap-northeast-1:111122223333:repository/my-app"
}
]
}
ecr:GetAuthorizationTokenis an account-level permission whoseResourcecan't be narrowed, so*is required; but the pull actions that actually fetch images can be limited to the target repository ARN. If you'd rather not store an IAM user's long-lived access key, consider a setup that assumes a role via OIDC.
For Docker-base-image-update operations, head to the Docker base-image update guide.
5.3 JFrog Artifactory (npm example)
registries:
artifactory-npm:
type: npm-registry
url: https://artifactory.example.com/artifactory/api/npm/npm-virtual
username: ${{secrets.DEPENDABOT_ARTIFACTORY_USER}}
password: ${{secrets.DEPENDABOT_ARTIFACTORY_TOKEN}}
replaces-base: true
Artifactory supports OIDC too. The stricter the governance requirements, the more it's worth choosing OIDC.
6. A copy-paste "everything" dependabot.yml
The practical pattern for handling multiple private registries in one file. Define registries at the top level and explicitly link only the registries each updates job uses.
version: 2
registries:
# ① AWS CodeArtifact(npm)— 可能なら静的トークンではなく OIDC 連携へ寄せる
codeartifact-npm:
type: npm-registry
url: https://my-domain-111122223333.d.codeartifact.ap-northeast-1.amazonaws.com/npm/my-repo/
token: ${{secrets.DEPENDABOT_CODEARTIFACT_TOKEN}}
# ② Amazon ECR(Docker)— 読み取り専用IAMの静的AWS認証
ecr:
type: docker-registry
url: 111122223333.dkr.ecr.ap-northeast-1.amazonaws.com
username: ${{secrets.DEPENDABOT_AWS_ACCESS_KEY_ID}}
password: ${{secrets.DEPENDABOT_AWS_SECRET_ACCESS_KEY}}
# ③ JFrog Artifactory(Maven)— 社内ミラーを唯一の取得元に
artifactory-maven:
type: maven-repository
url: https://artifactory.example.com/artifactory/libs-release
username: ${{secrets.DEPENDABOT_ARTIFACTORY_USER}}
password: ${{secrets.DEPENDABOT_ARTIFACTORY_TOKEN}}
replaces-base: true
# ④ 社内プライベート PyPI(pip)
internal-pypi:
type: python-index
url: https://pypi.example.com/simple
token: ${{secrets.DEPENDABOT_PYPI_TOKEN}}
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
registries:
- codeartifact-npm # このジョブは CodeArtifact だけを参照
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
registries:
- ecr
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
registries:
- artifactory-maven
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
registries:
- internal-pypi
# GitHub Packages はここに書かない(GITHUB_TOKEN で自動認証)
- package-ecosystem: "npm"
directory: "/packages/internal"
schedule:
interval: "weekly"
The least-privilege point: by default each
updatesjob has access to none of theregistries. Only the ones listed in that job'sregistries:are usable. The npm job can't see the ECR credentials, minimizing the exposure surface of credentials. Don't writeregistriesfor the GitHub Packages job — it's auto-authenticated withGITHUB_TOKEN(see section 3).
7. The reachability wall: self-hosted runners
So far it's about "authentication." A registry reachable only inside a VPC / internal network simply doesn't reach from GitHub-hosted runners. In this case, run Dependabot on a self-hosted runner.
7.1 When it becomes necessary
- An internet-private internal Artifactory / Nexus / private PyPI / internal Go proxy
- CodeArtifact / Artifact Registry reachable only via a VPC endpoint
7.2 The key points of setup (official)
- Prerequisites: Dependabot enabled, GitHub Actions enabled.
- Prepare the runner: register a self-hosted runner at the repository or organization level.
- Meet the environment requirements: an execution environment where Dependabot jobs run (the needed tools, network).
- Attach the
dependabotlabel: for a single repository, thedependabotlabel; for an organization, the prescribed label.
A gotcha (must-read): when you enable this feature, Dependabot jobs run only on runners with the
dependabotlabel. Forgetting the label is a serious pitfall where jobs keep queuing forever (= it looks like nothing happens). Always verify "does a labeled runner truly exist" before enabling.
7.3 Network and security design
- Network: the runner can reach both GitHub and the target registry. Arrange the firewall/proxy allowlist.
- Isolation: a self-hosted runner is an execution environment where Dependabot can touch outside the trust boundary (the updated dependencies). Isolate it from other uses and operate it at least privilege (both network and credentials). Don't run it where it can touch production secrets.
8. Security checklist
- All credentials in Dependabot secrets (not in Actions secrets, not hardcoded)
- OIDC for capable registries (CodeArtifact / Azure Artifacts / Cloudsmith / Artifact Registry / Artifactory) to eliminate static tokens
- Tokens at least privilege, read-only (only the scope needed to fetch updates)
- Use
GITHUB_TOKENauto-authentication for GitHub Packages, don't create extra PATs - Self-hosted runners isolated, at least privilege, verify the
dependabotlabel - On an auth error, isolate with the four-category error codes
To extend this configuration into dependency automation across your whole CI/CD, see the Dependabot production-operations guide; for errors beyond authentication and reachability, see the troubleshooting guide.