FROM node:22 — this one line brings the whole OS and system libraries into your container. No matter how cleanly you update the app's npm dependencies, if the underlying base image is old, the container's CVEs don't decrease. The base image is actually the biggest dependency.
Dependabot's docker ecosystem auto-follows this base image. This article explains Docker base-image updates at the implementation level, from a reproducibility and security view, as a topic of the Dependabot production-operations guide.
Rules for this article: ecosystem names and behavior are based on the GitHub official documentation and Dependabot's public info (as of June 2026). Docker Compose version updates went GA in February 2025. Always confirm the latest in the official supported ecosystems before production.
0. Why the base image is "the biggest dependency"
あなたのコンテナ = アプリコード
+ アプリ依存(npm/pip/gem …)← 多くの人がここだけ更新する
+ ベースイメージ(OS + システムライブラリ)← 実はここが最大
openssl, glibc, zlib — serious CVEs often appear on the system-library side. Since these are included in the base image, they aren't fixed unless you update the base image. Feeling safe after updating only the app dependencies is a half measure.
1. The basics of the docker ecosystem
Add docker (and docker-compose if needed) to dependabot.yml.
version: 2
updates:
- package-ecosystem: "docker"
directory: "/" # Dockerfile のある場所
schedule:
interval: "weekly"
commit-message:
prefix: "chore(docker)"
# docker-compose.yml のイメージも更新(2025年2月にGA)
- package-ecosystem: "docker-compose"
directory: "/"
schedule:
interval: "weekly"
Dependabot parses the FROM lines of Dockerfile and the image: of Compose, and opens a PR when a new tag comes out. The same mechanism applies to images in Kubernetes manifests and Helm-chart YAML too.
2. Tag updates vs digest pinning
Base-image references have levels of reproducibility.
| Reference | Example | Reproducibility | Note |
|---|---|---|---|
| Mutable tag | node:22 | Low | The contents can change even for the same tag (below) |
| Version-specified tag | node:22.11.0-bookworm-slim | Mid | More specific but the tag is mutable |
| Digest pin | node:22-slim@sha256:abc... | High | Immutable. Fixed to the exact image you audited |
The form of digest pinning is as follows.
# タグ+digest(再現性が高い)
FROM node:22-slim@sha256:1a2b3c4d...
# Dependabot は digest があれば、タグと digest を同時に更新する
If @sha256:... is attached, Dependabot updates the tag and digest together to the new values (exactly the same philosophy as SHA pinning for GitHub Actions). You can reconcile reproducibility (the same build yields the same result) and update automation.
3. Silent rebuild: why digest pinning works
"I specify node:22, so it's always the same" — this is wrong. A silent rebuild (the upstream maintainer swapping the image's contents while keeping the same tag) happens routinely. A rebuild to apply security patches is good, but if you only look at the tag, you can't tell when the contents changed.
Here digest pinning works.
- Pinning the digest means that even if the upstream swaps contents under the same tag, your build keeps using the fixed image (reproducibility).
- Running Dependabot daily brings a digest-update PR when the upstream issues a new digest. So "when and what changed" is visualized as a PR and taken in after review.
- As a result, you can structurally reduce image CVE rot (vulnerabilities accumulated by neglect).
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily" # digest 追従は日次が効く
commit-message:
prefix: "chore(docker)"
"Fixing for reproducibility" and "updating for freshness" aren't contradictory. Fix (digest pin) and update via PR (Dependabot) — this combination is the norm that satisfies reproducibility and security simultaneously.
4. Multi-stage and minimal images
A practical Dockerfile is multi-stage. Dependabot targets the FROM of each stage for updates.
# ---- build stage ----
FROM node:22-slim@sha256:aaa... AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- runtime stage(最小・非root) ----
FROM gcr.io/distroless/nodejs22-debian12@sha256:bbb... AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER nonroot
CMD ["dist/server.js"]
Design points (cost efficiency and security):
- Choosing a minimal image like distroless /
-slim/ alpine shrinks the attack surface, the number of CVEs, and the image size (= registry/transfer cost). - Digest-pin both stages and have Dependabot follow them.
- Run non-root (
USER nonroot) to minimize runtime privileges.
5. Private-registry base images
You can update base images in an internal ECR / Artifact Registry / Artifactory too. Define docker-registry authentication in registries.
version: 2
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}}
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
registries: ["ecr"]
ECR uses static AWS credentials; Artifact Registry / Artifactory can also use OIDC. For per-type authentication details and self-hosted runners, head to the private-registry authentication guide.
6. Auto-merge "carefully"
A base-image update is a change to the OS and system libraries. Since the impact is harder to read than an app-dependency patch, be one notch more careful with auto-merge.
- patch / within-same-major updates (e.g.
22.11.0→22.11.1, digest following) are auto-merge candidates if the build + smoke test passes. - Major updates (e.g.
debian 12 → 13,node 22 → 24) require human review since glibc or system-library incompatibilities can occur. - Pair with image scanning: Dependabot mainly does GitHub-Advisory-based dependency updates. To directly see vulnerabilities in the container's packages, add Trivy / Grype image scanning to CI, and on a digest-update PR confirm whether the scan result worsened (how to choose an SCA tool).
- name: Auto-merge docker patch / digest bumps
if: |
steps.meta.outputs.package-ecosystem == 'docker' &&
steps.meta.outputs.update-type == 'version-update:semver-patch'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
For the auto-merge token model and the safety valve (required checks), see the auto-merge guide.
7. Checklist
- Add
package-ecosystem: docker(anddocker-composeif needed) - Digest-pin the base image with
tag@sha256:...(reproducibility) - Follow the digest with a daily schedule as a silent-rebuild countermeasure
- A minimal runtime of distroless / slim / non-root in multi-stage
- Private images with
docker-registryauth (ECR = static AWS / Artifact Registry = OIDC) - Limit auto-merge to patch / digest following, human review for major
- Add Trivy / Grype image scanning to CI, with dependency updates and vulnerability detection as both wheels
8. FAQ
Q. Does Dependabot update even with FROM node:22 as-is?
A. It can follow tag updates, but reproducibility is low and you can't notice silent rebuilds. Digest-pin with tag@sha256:... and Dependabot updates the tag and digest together, reconciling reproducibility and freshness.
Q. Doesn't digest pinning stop updates? A. It doesn't. Dependabot opens a PR updating to the new digest/tag. A daily schedule follows silent rebuilds too.
Q. Can Docker Compose images be updated too?
A. They can. The docker-compose ecosystem went GA in February 2025. Use it alongside Dockerfile.
Q. May I auto-merge base images? A. Limit it to patch / digest following, and route OS major updates to human review. Incompatibilities like glibc can occur.
Q. Is Dependabot alone enough for container security? A. Dependabot mainly does dependency (GitHub Advisory) updates. For comprehensive scanning of the container's packages, pair it with Trivy/Grype. The role division is explained in the SCA-tool comparison.