Your Dockerfile lives at `docker/api.Dockerfile` but the build must COPY files from the repository root. How do you invoke `docker build` for that, and what context sources other than a local directory does `docker build` accept?
answer
- -f = which Dockerfile; positional = which context
- Dockerfile may live outside the context; COPY sources may not
- git URL fragment: #ref:subdir
- docker build - < Dockerfile = empty context
- BuildKit: <dockerfile>.dockerignore replaces the root one
basics
~20 sRun docker build -f docker/api.Dockerfile -t api . from the repository root: -f locates the Dockerfile, the positional argument sets the context. Besides a local directory, the context can be a Git repository URL, a tarball URL, or a tar/Dockerfile piped on stdin with -.
solid answer
~50 sSeparate the two arguments in your head: `-f` says *which Dockerfile*, the positional argument says *which context*. From the repository root: ``` docker build -f docker/api.Dockerfile -t myorg/api:dev --build-arg VERSION=1.4 . ``` The Dockerfile may live outside the context entirely — the client reads it directly — but every COPY source must still be inside the context. Other accepted context forms: - **Git URL**: `docker build https://github.com/org/repo.git#main:services/api` — the builder clones the repo, checks out the ref, and uses the given subdirectory as context. No local checkout needed. - **Tarball URL**: an `http(s)` URL to a `.tar.gz`, which is downloaded and unpacked as the context. - **stdin**: `docker build - < Dockerfile` builds with an empty context; `docker build -f - .` reads the Dockerfile from stdin with a local context. With BuildKit, a sibling `docker/api.Dockerfile.dockerignore` overrides the context-root ignore file — which is how per-service filtering works when several services share one root context.
code
bash · 11 lines# Dockerfile outside the context root, context = repo root
docker build -f docker/api.Dockerfile -t myorg/api:dev --build-arg VERSION=1.4 .
# context from a Git repo, ref and subdirectory
docker build -t api https://github.com/org/repo.git#main:services/api
# no context at all
docker build -t tiny - < Dockerfile
# extra named context, referenced as COPY --from=shared
docker build --build-context shared=../libs/common -t api .go deeper
Know that -f points at the Dockerfile and the last argument is the context, and that you build from the root when you need files from the root.
Add the common flags (-t, --build-arg, --target), the rule that COPY sources must stay inside the context, and awareness that Git and stdin contexts exist.
Discuss per-Dockerfile ignore files, named build contexts, credential implications of remote contexts, and why --build-arg is not a secret channel.
Frame it as repository and pipeline policy: which context each service builds from, how that bounds transfer cost and cache hit rate, and whether CI uses local or remote contexts.
## The two independent arguments The most common confusion in `docker build` is conflating the Dockerfile's location with the context. They are independent: - `-f, --file` — path to the Dockerfile. Defaults to `<context>/Dockerfile`. It is read by the **client** and may sit anywhere on your filesystem, including outside the context. - the positional argument — the **context**, the file tree that COPY and ADD can read from. So the standard monorepo invocation is run from the repository root: ``` docker build -f docker/api.Dockerfile -t myorg/api:dev . ``` Here `.` is the repo root, so `COPY libs/common /app/libs/common` resolves. Building from inside `docker/` instead would make the context that subdirectory, and every COPY of repository files would fail with 'forbidden path outside the build context'. Widening the context is the fix; `../` is never one. Naming convention matters for tooling: `api.Dockerfile` and `Dockerfile.api` are both used in the wild, but only the `<name>.Dockerfile` form pairs cleanly with BuildKit's per-Dockerfile ignore file, which is looked up as the Dockerfile path plus `.dockerignore` — `docker/api.Dockerfile.dockerignore`. When that file exists it **replaces** the context-root `.dockerignore` rather than adding to it, so it must be self-sufficient. ## The flags that travel with it - `-t, --tag` — name the result, repeatable: `-t app:1.4 -t app:latest`. Without it the image is untagged and only reachable by digest or ID. - `--build-arg KEY=VALUE` — supply values for `ARG` declared in the Dockerfile. Passing an undeclared name is a warning, not an error. These values are visible in image history, so they are for versions and mirrors, not secrets. - `--target <stage>` — stop at a named build stage. - `--no-cache`, `--pull` — force a full rebuild and re-resolve base images. - `--progress=plain` — unfold the log, which is how you actually read the `transferring context` figure and cache decisions. - `--build-context name=path|url` (BuildKit) — attach **additional** named contexts, referenced as `COPY --from=name`. It also lets you pin a stage or base image name to a local directory, which is useful for building a service against a locally-modified shared library without widening the primary context. ## Remote and streamed contexts **Git URL.** `docker build https://github.com/org/repo.git#branch:subdir` makes the builder clone the repository, check out `branch`, and treat `subdir` as the context. The fragment syntax is `#<ref>:<subdir>`, and both parts are optional. Useful for building something you have not checked out, and for reproducible CI builds pinned to a commit SHA. Note that the clone happens builder-side, so private repositories need credentials available there — this is a frequent stumbling block. **Tarball URL.** An `http(s)` URL ending in a tar archive is downloaded and extracted as the context. Handy for release artifacts. **stdin.** `docker build - < Dockerfile` sends a bare Dockerfile with an **empty** context — nothing local is available to COPY, which makes it fast and a clean test of whether a Dockerfile is self-contained. If stdin is a tar stream instead, it is used as the context. `docker build -f - .` is the mirror case: Dockerfile from stdin, local directory as context, useful for generated Dockerfiles in scripts. **A single remote file URL** as the positional argument is treated as a Dockerfile, again with no context. ## Choosing between them For a monorepo, the practical choice is between many small contexts (each service directory is its own context, simple and fast, but shared code must be vendored or published as a package) and one root context (shared code just works, but every service pays the walk and needs disciplined ignore files). BuildKit's named contexts sit in between: a small primary context plus explicit extra inputs, which keeps the payload minimal while still reaching shared directories. Remote Git contexts are attractive for CI because they remove the checkout step, but they trade away the ability to build uncommitted changes and move credential handling to the builder. Most teams use local contexts in CI for that reason and reserve Git contexts for ad-hoc or bootstrap builds. ## A note on `docker buildx build` Modern Docker routes `docker build` through BuildKit by default, and `docker buildx build` exposes the fuller flag set — multiple platforms, cache import/export, alternative outputs. The context semantics described here are identical between them.
- Building from `docker/` with `docker build -f api.Dockerfile .` fails on a COPY of a repository file. Why, and what is the fix?The context is `docker/`, so repository files above it were never transferred and the COPY is rejected as a path outside the build context. Run the build from the repository root with `-f docker/api.Dockerfile .`, or attach the needed directory as a named build context. Rewriting the COPY with `../` cannot work.
- You build from a Git URL and the build fails to fetch the repository. What is the likely cause?The clone happens on the builder, not on your machine, so your local SSH agent and credentials are irrelevant. Private repositories require credentials available to the builder, which is why most CI pipelines check out locally and pass a directory context instead.
saying these in an interview costs you the question
- Believing the Dockerfile must live inside the build context.
- Thinking `-f` changes the context as well as the Dockerfile location.
- Passing secrets via `--build-arg`, which persists them in image history.
- Assuming a Git-URL context uses your local credentials or your working-tree changes.
- Expecting a context-root `.dockerignore` to still apply when a `<dockerfile>.dockerignore` exists — the latter replaces it.