How to Fix 'Hash Sum Mismatch' in Docker apt-get update (Root Cause + CI/CD Prevention)
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5–10 mins
TL;DR
- What broke: Docker's cached APT index is stale or a mirror returned a package whose checksum doesn't match the Release file —
apt-get updatehard-fails and your image build dies. - How to fix it: Bust the Docker layer cache on the
apt-get updatestep, clean/var/lib/apt/lists/*before and after, and pin to a reliable mirror. - Fast path: Drop your failing
Dockerfileinto the Client-Side Sandbox above — it will auto-refactor theRUNblock with the correct cache-busting pattern without sending your file anywhere.
The Incident (What Does the Error Mean?)
Raw error output from a failing build:
Get:5 http://archive.ubuntu.com/ubuntu focal/main amd64 Packages [1,275 kB]
Err:5 http://archive.ubuntu.com/ubuntu focal/main amd64 Packages
Hash Sum mismatch
Hashes of expected file:
- Filesize:1275006 [weak]
- SHA256:e6b4...
Hashes of received file:
- SHA256:a1c9...
E: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/focal/main/binary-amd64/Packages.xz Hash Sum mismatch
E: Some index files failed to download. They have been ignored, or old ones used instead.
Immediate consequence: The RUN apt-get update layer exits non-zero. Docker build stops. Your CI job fails. No image is pushed. If this is a hotfix pipeline, you are now blocked.
The error means APT downloaded an index file from a mirror but its computed SHA-256 (or MD5) does not match the cryptographic hash published in the mirror's Release file. APT refuses to proceed — correctly — because using a tampered or partially-transferred index could result in installing wrong or malicious packages.
The Attack Vector / Blast Radius
This is primarily a build reliability failure, but it has a secondary security surface:
Stale Docker layer cache (most common): Docker cached the
apt-get updatelayer days or weeks ago. The mirror has since rotated its index. The cached layer holds an oldReleasefile. When a subsequentapt-get installin a separateRUNinstruction triggers a re-fetch, the hashes diverge. This is the classic mistake of splittingapt-get updateandapt-get installinto twoRUNcommands.Flaky or geo-routed mirror: CDN-backed mirrors (
archive.ubuntu.com,deb.debian.org) occasionally serve partially-replicated state during a mirror sync window. A build hitting a mid-sync mirror gets aPackages.xzthat doesn't yet match theReleasefile on the same edge node.Blast radius in CI/CD: Every parallel build job hitting the same mirror at the same time will fail simultaneously. In a Kubernetes-based CI cluster (Tekton, GitHub Actions self-hosted), this can take down your entire build fleet. Retrying without fixing the root cause just hammers the mirror.
Security implication: A network-level MITM (e.g., a compromised corporate proxy or a rogue Docker registry mirror) could deliberately serve a mismatched index to force a build failure or, if APT were misconfigured to ignore errors, to inject a backdoored package. APT's hash verification is the last line of defense here.
How to Fix It
Basic Fix — Cache-Bust and Clean in a Single RUN
The cardinal rule: apt-get update and apt-get install must be in the same RUN instruction, and /var/lib/apt/lists must be wiped at the end.
- RUN apt-get update
- RUN apt-get install -y curl git build-essential
+ RUN apt-get update --fix-missing && \
+ apt-get install -y --no-install-recommends curl git build-essential && \
+ rm -rf /var/lib/apt/lists/*
If the build is still failing due to a mid-sync mirror, force a clean fetch by adding --no-cache to the docker build command:
- docker build -t myapp:latest .
+ docker build --no-cache -t myapp:latest .
Or invalidate only the APT layer by bumping a build arg:
+ ARG APT_CACHE_DATE=2024-01-01
RUN apt-get update && apt-get install -y ...
Change APT_CACHE_DATE in your CI invocation to force re-execution of that layer only.
Enterprise Best Practice — Mirror Pinning + Retry Logic + Verified Sources
For production Dockerfiles that must be reproducible and resilient:
- FROM ubuntu:22.04
- RUN apt-get update && apt-get install -y curl
+ FROM ubuntu:22.04
+
+ # Pin to a stable, well-maintained mirror. Replace with your regional mirror.
+ RUN sed -i 's|http://archive.ubuntu.com|http://us.archive.ubuntu.com|g' /etc/apt/sources.list
+
+ # Single atomic RUN: update, install, clean. Never split these.
+ RUN apt-get update -o Acquire::Retries=3 && \
+ apt-get install -y --no-install-recommends \
+ curl \
+ ca-certificates && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Key flags explained:
Acquire::Retries=3— APT will retry failed fetches 3 times before giving up, handling transient mirror sync windows.--no-install-recommends— reduces attack surface and image size.apt-get clean && rm -rf /var/lib/apt/lists/*— ensures the layer contains zero cached index data, preventing stale-hash issues in child images.
For air-gapped or regulated environments, host a private APT mirror (Artifactory, Nexus, apt-mirror) and point your base images at it. This eliminates public mirror flakiness entirely.
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Hadolint in your pipeline (catches the split RUN anti-pattern):
# .github/workflows/lint.yml
- name: Lint Dockerfile
uses: hadolint/[email protected]
with:
dockerfile: Dockerfile
failure-threshold: warning
Hadolint rule DL3009 flags apt-get update not paired with apt-get install in the same layer. Rule DL3008 enforces pinned package versions.
2. Enforce with OPA/Conftest against Dockerfile policies:
# policy/dockerfile.rego
package dockerfile
deny[msg] {
input[i].Cmd == "run"
val := input[i].Value
contains(val[_], "apt-get update")
not contains(val[_], "rm -rf /var/lib/apt/lists")
msg := "apt-get update RUN block must clean /var/lib/apt/lists/* in the same layer"
}
3. Scheduled base image rebuilds: Do not rely on Docker's layer cache for base images older than 7 days. Use a weekly scheduled pipeline that rebuilds all base images with --no-cache. This keeps your APT index fresh and catches upstream CVEs.
4. Private mirror for reproducibility: In enterprise environments, route all APT traffic through Artifactory or Nexus with a cron-synced upstream. Pin your sources.list in a controlled base image layer. Zero dependency on public mirror availability during builds.