Polyaxon v3 is coming →

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.

March 21, 2026by Polyaxon
Lint ML Dockerfiles with Hadolint

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 Dockerfile

The 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 Dockerfile

Start 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:

ControlQuestion it answers
Dependency lock and provenanceWhich source and version produced each dependency?
Secret scanningDid credentials enter the build context or layers?
Vulnerability scanningWhich known issues exist in OS and application packages?
Software bill of materialsWhat components are present in the final image?
Signature or attestation verificationWho built the image and through which process?
Runtime smoke testDoes the image start and execute the intended workload?
Accelerator validationDoes 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.