✦ For everyone, free.

Practical knowledge for real and everyday life

Home

19.1.1.1 Build Dockerfile Input

A focused guide to Build Dockerfile Input, connecting core concepts with practical Docker and container operations.

Build Dockerfile input covers the several distinct ways a build's actual Dockerfile content can be supplied beyond the conventional, default Dockerfile file sitting at the build context root, including custom paths, piping content directly through standard input, and the syntax directive comment that pins which specific BuildKit frontend version interprets the file.

The default Dockerfile location

Without any explicit flag, docker build looks for a file literally named Dockerfile at the root of the specified build context:

docker build -t my-api .

This default assumption, a file named exactly Dockerfile in the context root, is what most projects rely on without ever needing to specify anything else, but understanding the explicit alternatives matters once a project's structure deviates from this single, simple convention.

Specifying a custom Dockerfile path

The -f flag points the build at a differently named or located file, useful for a project maintaining several distinct Dockerfiles for different purposes within the same repository:

docker build -f Dockerfile.prod -t my-api:prod .
docker build -f docker/Dockerfile.dev -t my-api:dev .

As covered in dedicated build path content, the file specified through -f does not need to live anywhere within the build context directory itself; only the context argument (the final positional argument) determines what gets sent to the daemon, while -f can point at a Dockerfile located entirely outside that context.

Piping Dockerfile content through standard input

A Dockerfile's content can be supplied directly through stdin rather than from any file on disk at all, using a hyphen as a special context argument indicating no actual build context directory is being supplied:

cat Dockerfile | docker build -t my-api -
docker build -t my-api - <<EOF
FROM alpine:3
RUN echo "hello"
EOF

This is genuinely useful for a build that needs no actual file copying from a context at all, just executing a sequence of instructions, often in scripting or testing scenarios where constructing a Dockerfile inline is more convenient than writing and managing a separate file.

Combining a remote context with an explicit Dockerfile path

When the build context itself is a remote Git repository URL, -f can specify a Dockerfile path relative to that remote repository's own structure, rather than relative to anything on the local filesystem:

docker build -f docker/Dockerfile.prod https://github.com/example/my-api.git#main

This combination is useful for CI or automation scenarios building directly from a repository reference without a local checkout, while still needing to specify a non-default Dockerfile location within that remote repository's own directory structure.

The syntax directive for pinning the BuildKit frontend

A special comment at the very top of a Dockerfile, the syntax directive, pins which specific version of the BuildKit Dockerfile frontend interprets the file, which matters because newer Dockerfile features, certain heredoc syntax, specific mount types, sometimes require a frontend version beyond what an older Docker installation's bundled default would otherwise use:

# syntax=docker/dockerfile:1
FROM node:20
# syntax=docker/dockerfile:1.4
FROM node:20
RUN <<EOF
apt-get update
apt-get install -y curl
EOF

This directive must appear as the very first line of the file, before any other content including comments, and pinning to a specific, known frontend version (rather than always floating to whichever is newest) provides the same reproducibility benefit covered for other pinning practices generally, ensuring the Dockerfile's own interpretation does not silently change as the underlying BuildKit frontend itself receives updates over time.

Verifying which frontend version is actually in use

Confirming the actual frontend version a build used can be useful when investigating whether a specific, newer Dockerfile feature is actually available in a given build environment:

docker buildx build --progress=plain -t my-api . 2>&1 | grep -i "syntax"

This kind of direct verification is worth doing when a Dockerfile using a newer syntax feature behaves unexpectedly on one machine but not another, since the actual frontend version resolved can genuinely differ between environments with different Docker or BuildKit versions installed.

Common mistakes

  • Assuming a Dockerfile must always be named exactly Dockerfile and located at the build context root, without knowing about the -f flag for any project structured differently.
  • Not knowing that piping a Dockerfile through stdin with a hyphen context is a valid, useful approach for scripting or testing scenarios that need no actual file-based build context.
  • Forgetting that a Dockerfile path specified alongside a remote Git repository context resolves relative to that remote repository's structure, not the local filesystem.
  • Omitting the syntax directive comment, leaving the Dockerfile's interpretation tied to whatever frontend version happens to be the current default, rather than a deliberately pinned, known one.
  • Placing the syntax directive comment anywhere other than the very first line of the file, where it has no effect at all.

Build Dockerfile input supports considerably more flexibility than the simple default convention most projects rely on, custom paths through -f, stdin piping for contextless builds, remote-context-relative paths, and the syntax directive for deliberately pinning frontend behavior, each addressing a specific scenario beyond the basic, single-file-at-the-context-root assumption.