Skip to main content
tally supports 22/22 BuildKit checks. 5 are captured directly from BuildKit’s parser; 17 are reimplemented as static rules so they work without running Docker/BuildKit.
BuildKit checks come from Docker’s official build checks reference. tally integrates them in two ways:
  • Implemented by tally — BuildKit normally runs these during LLB conversion (i.e., when actually building). tally reimplements them as pure static checks so they catch issues without a Docker daemon.
  • Captured from BuildKit parser — These are emitted by BuildKit during Dockerfile parsing and forwarded directly to tally’s diagnostic pipeline.
All 11 auto-fixable rules are marked with 🔧. Run tally lint --fix to apply safe fixes automatically.

Implemented by tally

These 17 checks correspond to BuildKit’s LLB-conversion checks. tally runs them statically, so you get full coverage without Docker or BuildKit installed.
All commands within the Dockerfile should use the same casing (either upper or lower).Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: consistent-instruction-casing
# Violation: mixed casing
FROM alpine
run apk add curl
COPY . /app
cmd ["./app"]
Attempting to COPY a file that is excluded by .dockerignore.Severity: Warning (enabled by default) · Auto-fixable: NoDocker docs: copy-ignored-file
# .dockerignore contains: *.log

# Violation: copying a file excluded by .dockerignore
COPY app.log /app/app.log
Stage names should be unique.Severity: Error (enabled by default) · Auto-fixable: NoDocker docs: duplicate-stage-name
# Violation: two stages share the name "builder"
FROM alpine AS builder
RUN echo "first"

FROM ubuntu AS builder
RUN echo "second"
IP address and host-port mapping should not be used in EXPOSE. This will become an error in a future release.Severity: Warning (enabled by default) · Auto-fixable: NoDocker docs: expose-invalid-format
# Violation: host-port mapping in EXPOSE
EXPOSE 0.0.0.0:8080
Protocol in EXPOSE instruction should be lowercase.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: expose-proto-casing
# Violation: protocol in uppercase
EXPOSE 80/TCP
EXPOSE 443/UDP
FROM --platform flag should not use a constant value.Severity: Off by default · Auto-fixable: NoDocker docs: from-platform-flag-const-disallowed
# Violation: hardcoded platform instead of using build arg
FROM --platform=linux/amd64 alpine
Base image platform does not match expected target platform.Severity: Off by default · Auto-fixable: No
# Violation: explicit platform conflicts with the target platform
FROM --platform=linux/arm64 node:20 AS builder
Default value for a global ARG results in an empty or invalid base image name.Severity: Error (enabled by default) · Auto-fixable: NoDocker docs: invalid-default-arg-in-from
# Violation: ARG default is empty, making the image name invalid
ARG BASE_IMAGE=""
FROM ${BASE_IMAGE}
JSON arguments are recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals.Severity: Info (enabled by default) · Auto-fixable: YesDocker docs: json-args-recommended
# Violation: shell form — PID 1 is /bin/sh, not the process
ENTRYPOINT myapp --config /etc/myapp.conf
CMD myapp
Legacy key/value format with whitespace separator should not be used.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: legacy-key-value-format
# Violation: legacy whitespace-separated ENV and LABEL syntax
ENV MY_VAR my_value
LABEL maintainer John Doe
Multiple instructions of the same type should not be used in the same stage.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: multiple-instructions-disallowed
FROM alpine
# Violation: two CMD instructions; only the last takes effect
CMD ["echo", "first"]
CMD ["echo", "second"]
Setting --platform to the predefined $TARGETPLATFORM in FROM is redundant as it is the default behavior.Severity: Warning (enabled by default) · Auto-fixable: NoDocker docs: redundant-target-platform
# Violation: $TARGETPLATFORM is already the default
FROM --platform=$TARGETPLATFORM alpine
Reserved words should not be used as stage names.Severity: Error (enabled by default) · Auto-fixable: NoDocker docs: reserved-stage-name
# Violation: "scratch" is a reserved stage name
FROM alpine AS scratch
RUN echo "building"
Sensitive data should not be used in the ARG or ENV commands.Severity: Warning (enabled by default) · Auto-fixable: NoDocker docs: secrets-used-in-arg-or-env
# Violation: secret value baked into the image layer
ARG AWS_SECRET_ACCESS_KEY
ENV DATABASE_PASSWORD=supersecret
FROM command must use declared ARGs.Severity: Warning (enabled by default) · Auto-fixable: NoDocker docs: undefined-arg-in-from
# Violation: IMAGE_TAG is not declared before use in FROM
FROM alpine:${IMAGE_TAG}
Variables should be defined before their use.Severity: Warning (enabled by default) · Auto-fixable: NoDocker docs: undefined-var
FROM alpine
# Violation: $APP_DIR was never defined with ARG or ENV
COPY . $APP_DIR
Relative WORKDIR without a prior absolute WORKDIR can have unexpected results if the base image changes.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: workdir-relative-path
FROM alpine
# Violation: relative path depends on the base image's CWD
WORKDIR app

Captured from BuildKit parser

These 5 checks are emitted by BuildKit during Dockerfile parsing. tally captures them directly and includes them in its diagnostic output alongside statically implemented rules.
The as keyword should match the case of the from keyword.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: from-as-casing
# Violation: FROM is uppercase but AS is lowercase (or vice versa)
FROM alpine as builder
# or
from alpine AS builder
Comments for build stages or arguments should follow the format: # <arg/stage name> <description>. If this is not intended to be a description comment, add an empty line or comment between the instruction and the comment.Severity: Warning · Default: Off (experimental) · Auto-fixable: YesDocker docs: invalid-definition-description
# Violation: comment does not match the stage name immediately below
# This is the production builder
FROM alpine AS builder
The MAINTAINER instruction is deprecated; use a label instead to define an image author.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: maintainer-deprecated
FROM alpine
# Violation: MAINTAINER is deprecated
MAINTAINER Jane Doe <jane@example.com>
Empty continuation lines will become errors in a future release.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: no-empty-continuation
FROM alpine
# Violation: trailing backslash on an otherwise empty continuation line
RUN echo "hello" \
    \
    && echo "world"
Stage names should be lowercase.Severity: Warning (enabled by default) · Auto-fixable: YesDocker docs: stage-name-casing
# Violation: stage name uses mixed or uppercase letters
FROM alpine AS Builder
FROM ubuntu AS PROD