Lint ML Dockerfiles with Hadolint
Use Hadolint to catch Dockerfile problems early while keeping base-image policy, dependency pinning, security scanning, and runtime validation separate.

ML Dockerfiles accumulate operating-system packages, compiler toolchains, model dependencies, accelerator libraries, notebooks, and application code. Small build mistakes become large images, slow cold starts, inconsistent environments, and difficult security reviews.
Hadolint statically analyzes Dockerfiles and the shell commands inside RUN instructions. It gives teams a fast, reviewable baseline, but it is one control in a larger image lifecycle—not a security scanner or proof that the workload functions.
Run it locally first
Point Hadolint at the same Dockerfile used by automation:
hadolint DockerfileThe output identifies a line, rule code, severity, and description. Rules prefixed with DL come from Hadolint; shell-related findings can come from ShellCheck integration.
Read the rule's rationale before changing the file. A lint-clean but unreadable workaround is not an improvement, and a valid exception should be documented rather than hidden.
Fix the Dockerfile, not only the warning
A maintainable ML image usually starts from patterns like these:
# syntax=docker/dockerfile:1
FROM python:3.13-slim@sha256:REPLACE_WITH_APPROVED_DIGEST
WORKDIR /app
COPY requirements.txt ./
RUN python -m pip install --no-cache-dir --requirement requirements.txt
COPY src/ ./src/
USER 65532:65532
CMD ["python", "-m", "src.train"]The placeholder digest must be replaced with a real digest approved for the target architecture. Pin Python dependencies through your chosen lock or constraints workflow as well; a pinned base image does not make requirements.txt reproducible.
This layout gives Hadolint and reviewers explicit decisions to inspect: base identity, working directory, dependency layer, final user, and exec-form command.
Set a shared policy
Decide which severity fails automation and which rules need project-specific treatment. Keep that configuration in version control. A typical CI command might be:
hadolint --failure-threshold warning DockerfileStart with visibility if an existing repository has many findings, then ratchet toward the agreed threshold. Failing every historical issue on day one often encourages broad ignores instead of useful remediation.
When ignoring a rule, keep the scope narrow and explain the reason next to the exception or in reviewed configuration. Include an owner and revisit condition for exceptions tied to a temporary base image or package repository.
Keep ML-specific tradeoffs explicit
Some ML images need compilers, system libraries, or accelerator runtimes. Do not suppress size and pinning warnings simply because the image contains ML code. Instead:
- use multi-stage builds when build tools are not needed at runtime;
- start from the smallest vendor image that supports the required stack;
- remove package-manager metadata in the same layer;
- separate development and production images;
- keep large model weights outside the application image unless immutability or startup requirements justify bundling them;
- record the final image digest with the experiment.
For build-cache design, see Docker build caching for ML workloads.
Add the controls Hadolint does not provide
Hadolint does not determine whether an image contains a known vulnerability, leaked secret, malicious package, wrong architecture, or broken CUDA stack. Pair linting with:
| Control | Question it answers |
|---|---|
| Dependency lock and provenance | Which source and version produced each dependency? |
| Secret scanning | Did credentials enter the build context or layers? |
| Vulnerability scanning | Which known issues exist in OS and application packages? |
| Software bill of materials | What components are present in the final image? |
| Signature or attestation verification | Who built the image and through which process? |
| Runtime smoke test | Does the image start and execute the intended workload? |
| Accelerator validation | Does the image work with the destination driver and devices? |
Keep these results attached to the image digest. Scanning a mutable tag and deploying a later image under the same tag breaks the evidence chain.
Validate with representative Polyaxon operations
Use the linted image in a small Polyaxon operation that exercises its real entrypoint, connections, resources, and artifact output. For accelerator images, run a short device check and framework computation on the target node class. For distributed images, validate worker startup and network requirements.
Promote the immutable digest only after the checks succeed. Polyaxon run metadata then connects image identity to code, parameters, environment, metrics, logs, and outputs.
Hadolint is most valuable when it turns Dockerfile expectations into immediate feedback. Keep the rules understandable, exceptions narrow, and the rest of the supply-chain and runtime evidence explicit.