Skip to content

hadolint/DL3057

HEALTHCHECK instruction missing.

Property Value
Severity Ignore (off by default)
Category Best Practice
Default Off

Description

This is an optional rule. When it is required to define a health check (e.g. by company policy), it must not be omitted.

This rule is disabled by default because a HEALTHCHECK is not desirable in all circumstances. Images used with Kubernetes do not benefit from a HEALTHCHECK instruction, as Kubernetes brings its own mechanisms.

Examples

Problematic code

FROM busybox

Correct code

FROM busybox
HEALTHCHECK CMD /bin/health

or:

FROM busybox
HEALTHCHECK NONE

tally enhancements

Smart suppression

tally automatically suppresses this rule when the Dockerfile shows strong signals that a HEALTHCHECK would not be beneficial:

Serverless / FaaS base images

Containers built for serverless platforms have their lifecycle managed externally — the platform decides when to start, stop, and replace function instances. A container-level HEALTHCHECK is ignored in these environments.

Platform Suppressed image patterns
AWS Lambda public.ecr.aws/lambda/*, gallery.ecr.aws/lambda/*, amazon/aws-lambda-*
Azure Functions mcr.microsoft.com/azure-functions/*
OpenFaaS openfaas/of-watchdog, openfaas/classic-watchdog (including ghcr.io variants)

If any stage in the Dockerfile uses a recognized serverless base image, the violation is suppressed for the entire file. Multi-stage builds that pull from a Lambda image in one stage and copy artifacts into another are still covered because the presence of the serverless image signals the target runtime.

Serverless framework entrypoints

When the final stage's CMD or ENTRYPOINT invokes a known serverless function framework, the container is a short-lived function handler managed by the platform — not a service that benefits from HEALTHCHECK.

Framework Example
Google Cloud Functions (functions-framework) CMD ["functions-framework", "--target=hello"]

The exec prefix commonly used in shell form is handled:

CMD exec functions-framework --target=hello --port=$PORT

Interactive / shell-only containers

When the final stage's CMD or ENTRYPOINT resolves to a bare interactive shell (sh, bash, zsh, ash, dash, fish, csh, tcsh, ksh), the container is clearly not a long-running service — there is no endpoint to health-check.

Recognized patterns:

CMD ["bash"]               # exec form
CMD bash                   # shell form
CMD ["bash", "-l"]         # shell with flags (still interactive)
ENTRYPOINT ["/bin/sh"]     # entrypoint shell

Not suppressed when the shell is used to execute a command:

CMD ["bash", "-c", "my-app"]   # runs my-app, not interactive

If an ENTRYPOINT is present, it takes precedence over CMD (matching Docker runtime semantics).

No explicit CMD/ENTRYPOINT (external parent delegation)

When the final stage has no CMD or ENTRYPOINT instruction and its base is an external image (not another build stage), the image delegates run orchestration to its parent. In these cases the parent likely also defines a HEALTHCHECK, so flagging the child produces false positives. The violation is suppressed.

FROM nginx:latest
RUN echo "custom config" > /etc/nginx/conf.d/default.conf
EXPOSE 80
# No CMD — nginx base image provides CMD and likely HEALTHCHECK

This does not apply when the final stage inherits from a prior build stage (FROM <stage-name>), because CMD/ENTRYPOINT are inherited from the prior stage and the image is not opaque:

FROM alpine AS base
CMD ["my-app"]
FROM base
RUN echo "setup"
# DL3057 still fires — CMD inherited from "base", image is not opaque

Explicit opt-out with HEALTHCHECK NONE

HEALTHCHECK NONE is treated as a deliberate opt-out. When present in any stage, DL3057 is fully suppressed — no fast-path violation is emitted and no async registry checks are planned. This matches Docker's semantics where HEALTHCHECK NONE explicitly disables health checking.

Async registry resolution

tally extends this rule with async registry resolution (enabled with --slow-checks):

  • Base image inspection: For each external base image, tally checks if it already defines a HEALTHCHECK in its image metadata. If so, the violation is suppressed since the health check is inherited. This check is skipped when any explicit HEALTHCHECK instruction (CMD or NONE) is already present.
  • Cross-rule awareness: buildkit/MultipleInstructionsDisallowed may still flag duplicate HEALTHCHECK instructions even when DL3057 is suppressed.

Reference