docker build -f reference
Last reviewed on 2026-08-27
Building from a Dockerfile that is not called Dockerfile, or does not live in the context root.
Syntax
-f is the short form of --file. It tells docker build which file to read as the Dockerfile. The default, when you omit it, is Dockerfile in the root of the build context.
Every docker build invocation needs two independent things: a Dockerfile (the recipe) and a build context (the directory of files the recipe may copy from). By default Docker assumes they live together — docker build . means "read ./Dockerfile, use . as the context". The -f flag breaks that assumption apart, which is what you want as soon as a repository has more than one Dockerfile.
# Default: ./Dockerfile, context is .
docker build -t myapp .
# A differently named file in the same directory
docker build -f Dockerfile.prod -t myapp:prod .
# A Dockerfile in a subdirectory, but the whole repo as context
docker build -f services/api/Dockerfile -t api .
# A Dockerfile outside the context entirely
docker build -f /shared/templates/node.Dockerfile -t myapp ./services/api
The -f path is not relative to the context
This is the single most common source of confusion, and the answer changed with BuildKit. With the modern builder — the default since Docker 23 — the -f path is resolved relative to your current working directory, exactly like any other shell argument. It has nothing to do with the context argument. The classic pre-BuildKit builder required the Dockerfile to sit inside the context; BuildKit does not.
The context argument controls something separate: which files COPY and ADD can reach. Paths inside the Dockerfile are always resolved against the context root, never against the Dockerfile's own location:
docker build -f services/api/Dockerfile -t api .
# services/api/Dockerfile — paths are relative to the context root (.), not to services/api/
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./ # ← reads ./package.json at the repo root
COPY services/api/src ./src # ← full path from the repo root
RUN npm ci --omit=dev
CMD ["node", "src/server.js"]
If you instead run docker build -f services/api/Dockerfile ./services/api, the same COPY package.json line now reads services/api/package.json. Changing the context silently changes what every COPY in the file means — so when a build suddenly reports "/package.json": not found, check the context argument before you touch the Dockerfile.
Choosing the context for a monorepo
Two layouts dominate, and the trade-off is real:
| Command | What COPY can reach | Cost |
|---|---|---|
docker build -f services/api/Dockerfile ./services/api |
Only files under services/api. |
A tight, fast context. Cannot copy a root lockfile or a shared packages/ library. |
docker build -f services/api/Dockerfile . |
The entire repository. | Shared code and root lockfiles work. The whole repo is transferred, and a change anywhere can invalidate the cache unless .dockerignore is tight. |
For a workspace-based project (npm workspaces, pnpm, Go modules, Cargo), the repo-root context is usually the right answer, because the lockfile that makes the build reproducible lives at the root. Pair it with a strict .dockerignore so the cost stays low:
# .dockerignore at the repo root
**/node_modules
**/dist
**/.next
.git
BuildKit also supports a per-Dockerfile ignore file. With -f services/api/Dockerfile, it looks for services/api/Dockerfile.dockerignore first and only falls back to the root .dockerignore if that file is absent — which lets each service in a monorepo trim the context its own way.
Naming conventions for multiple Dockerfiles
Docker imposes no naming rules beyond "a readable file", but two conventions are widespread and both work with -f:
Dockerfile.prod,Dockerfile.dev,Dockerfile.test— suffix style. Sorts next to the plainDockerfile, and most editors still apply Dockerfile syntax highlighting.prod.Dockerfile,dev.Dockerfile— prefix style. Some editors only recognise this form; pick whichever your tooling highlights.
Before you create a second Dockerfile, though, check whether a multi-stage build would do the job. One file with dev, test and production stages selected by --target shares the dependency-install layer between all three, which two separate files cannot do:
# One Dockerfile, three outputs, one shared cache
docker build --target dev -t myapp:dev .
docker build --target test -t myapp:test .
docker build --target production -t myapp:1.0 .
Building from stdin
Pass -f - to read the Dockerfile from standard input. The context argument still applies, so this is a way to build against a real directory with a Dockerfile that is generated on the fly and never written to disk:
# From a file, without that file being part of the context
docker build -f - -t myapp . < Dockerfile.prod
# From a heredoc
docker build -f - -t myapp . <<'EOF'
FROM alpine:3.20
COPY . /app
CMD ["/app/run.sh"]
EOF
If you need no context at all — a Dockerfile with no COPY from the local filesystem — pipe the file in and pass - as the context. Docker then reads the Dockerfile from stdin and uses an empty context, which is the fastest possible build:
echo 'FROM alpine:3.20
RUN apk add --no-cache curl' | docker build -t curly -
Common errors
| Message | Cause |
|---|---|
failed to read dockerfile: open Dockerfile: no such file or directory | No -f was given and the context root has no Dockerfile. Add -f path/to/your/file. |
unable to prepare context: unable to evaluate symlinks in Dockerfile path | The -f path itself is wrong — a typo, or a path written relative to the context instead of to your shell's working directory. |
"/package.json": not found on a COPY | The context argument does not contain that file. The COPY path is relative to the context root, not to the Dockerfile's directory. |
"docker build" requires exactly 1 argument | The context argument is missing. -f consumes the value after it, so the trailing . is still required. |
| Build succeeds but copies stale files | An over-broad context plus a missing .dockerignore: a dist/ or node_modules/ from the host was copied in over the freshly built one. |
Related
docker buildcommand reference — the full flag list.docker build -treference — naming and tagging what you just built.- Build context reference — what the context actually is and what it costs.
.dockerignorereference — keeping a repo-root context cheap.- Multi-stage builds tutorial — usually a better answer than a second Dockerfile.