hadolint DL3018 failed: pin versions in apk add: docker review blocked
Fixes a hadolint DL3018 failure by pinning explicit versions on every apk add package in the Dockerfile. Use when the Docker review gate flags unpinned apk packages. Triggers: DL3018 warnings, hadolint failing the review step. Not for apt-get pinning (DL3008), pip pinning (DL3013), or hadolint config problems.
TL;DR
apk add curl installs whatever is newest today, so the image is not reproducible. Pin each package to an exact version like apk add curl=8.5.0-r0, rebuild, and the DL3018 warning clears.
The error
Dockerfile:5 DL3018 warning: Pin versions in apk add. Instead of `apk add [package]` use `apk add [package]=[version]`Steps
1. Find the current version of each package
Check the Alpine package index for the base image's Alpine release, or run apk list inside a container from that image. Expected output: exact version strings like 8.5.0-r0 for each package the Dockerfile installs.
2. Pin the versions in the Dockerfile
Change apk add curl jq to apk add curl=8.5.0-r0 jq=1.7.1-r0, keeping the existing --no-cache flag. Expected output: every package in the apk add line carries a =[version] suffix.
3. Rebuild the image
Run docker build as the review pipeline does. Expected output: the build succeeds with the pinned versions installed.
4. Re-run hadolint
Run hadolint on the Dockerfile again, or push and let the review step run. Expected output: no DL3018 findings and the review gate passes.
When to use
- hadolint reports DL3018 on a Dockerfile
- The Docker review step fails on unpinned apk packages
- Reproducible image builds are required
When not to use
- DL3008 (apt-get pinning; the syntax is
package=versionbut the fix context differs) - DL3013 (pip install pinning)
- hadolint failing to parse the Dockerfile at all
Tool compatibility
- hadolint v2, via the CLI, the reviewdog action, or the VS Code extension
- Alpine 3.18/3.19/3.20 package indexes; version strings are Alpine-release-specific
- Works with BuildKit and classic docker build
Variant phrasings
hadolint DL3008 pin versions in apt-get
Debian-flavored sibling of this rule. Pin with apt-get install -y curl=8.5.0-1 and add rm -rf /var/lib/apt/lists/* in the same layer.
hadolint DL3013 pin versions in pip
Pin in requirements or inline: pip install requests==2.31.0.
DL3018 on a virtual package install
Pin the real packages, not the virtual name: apk add --virtual .build-deps gcc=13.2.1_git20231014-r0.
Why it happens
Unpinned installs resolve to the newest package in the index at build time, so the same Dockerfile produces different images on different days. That breaks reproducibility and can silently pull in a breaking or vulnerable package version. DL3018 exists to force the version into the file where reviewers can see it.
Edge cases
- Pinned versions disappear when Alpine drops old packages from the index; bump the pin when the build starts 404ing on the package URL.
- Multi-stage builds: pin in every stage that runs apk add, not just the final one.
- Version strings differ between Alpine releases, so a pin that works on 3.19 may not exist on 3.20. Match the pin to the base image tag.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_G-uAFcFPI6mM3QRUk7lQsQ