skip to content

Besides a previously declared build stage, what else can the `--from` flag on a Dockerfile COPY instruction refer to, and what are the rules for naming and referencing stages?

level: middleimportance: should knowfreq 55%

answer

  1. --from = stage name, stage index, or external image
  2. Names case-insensitive, must be declared earlier
  3. External image = filesystem read only, no ENV/ENTRYPOINT
  4. Source path resolves in the source root, not WORKDIR
  5. Unreferenced stages never run

basics

~20 s

COPY --from accepts a stage name declared with FROM ... AS name, a stage index (--from=0), or any external image reference such as --from=nginx:1.27. Source paths are resolved inside that stage or image, not in the build context.

solid answer

~50 s

`COPY --from=<source>` accepts three kinds of source: 1. **A named stage** — `FROM golang:1.22 AS builder` then `COPY --from=builder /out/app /app`. Names are case-insensitive and must be unique. 2. **A stage index** — `--from=0` means the first stage. Works, but is brittle when stages are reordered; prefer names. 3. **An external image** — `COPY --from=nginx:1.27 /etc/nginx/nginx.conf /etc/nginx/`, or `--from=alpine:3.20 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/`. The image is pulled and used purely as a filesystem to read from; none of its metadata (ENV, ENTRYPOINT, layers) enters your image. Paths after `--from` resolve inside that stage's or image's root filesystem, **not** in the build context, and a leading `/` is relative to that root. You may copy from any earlier stage, not just the one you are descended from. With BuildKit, stages that nothing references are never executed at all.

code

dockerfile · 12 lines
dockerfile
FROM node:22 AS build
WORKDIR /src
COPY . .
RUN npm ci && npm run build

FROM scratch AS certs
COPY --from=alpine:3.20 /etc/ssl/certs/ca-certificates.crt /ca.crt

FROM gcr.io/distroless/nodejs22
COPY --from=build /src/dist /app
COPY --from=certs /ca.crt /etc/ssl/certs/ca-certificates.crt
CMD ["/app/server.js"]

go deeper

for a junior

Know the named-stage form and that the source path lives inside that stage. Mention that --from can also name an external image.

for a middle

Cover all three forms, path resolution against the source root, uniqueness/case rules for names, and why indexes are brittle.

for a senior

Explain the stage graph: any earlier stage is reachable, unreferenced stages are skipped, and that skipping is why a test stage doesn't gate a --target prod build unless you make it a dependency.

for a principal

Discuss it as dependency policy — pinning external --from images by digest, whether tool images are a supply-chain surface, and standardising a shared deps stage across service Dockerfiles.

## The three source forms `COPY` normally reads from the build context — the files sent to the builder. Adding `--from` redirects the read to a different filesystem. **Named stage.** Declare with `AS`: ``` FROM maven:3.9-eclipse-temurin-21 AS build ... FROM eclipse-temurin:21-jre COPY --from=build /src/target/app.jar /app/app.jar ``` Stage names are case-insensitive (`AS Build` and `--from=build` match), must be unique within the Dockerfile, and may only be referenced by instructions that appear *after* the stage is declared. Forward references are an error, and so are cycles. **Stage index.** `--from=0` refers to the first `FROM` in the file, `--from=1` to the second, and so on. This exists because named stages were added after multi-stage builds themselves. It still works, but any inserted stage silently shifts every index — a genuinely nasty bug class. Always name stages. **External image.** `--from=<image reference>` pulls that image and reads files out of it: ``` COPY --from=ghcr.io/org/tools:1.4 /usr/local/bin/migrate /usr/local/bin/migrate COPY --from=alpine:3.20 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ ``` This is a filesystem read, nothing more. The referenced image's `ENV`, `ENTRYPOINT`, `USER`, labels and layers do not become part of yours; only the bytes you copy do, and they land in a fresh layer of your image. Typical uses: grabbing CA certificates or timezone data into a `scratch` image, lifting a single static CLI (a migration tool, `grpc_health_probe`, `tini`) without installing a package manager, or pulling a config file from a vendor image. A cleaner variant of the same idea is to alias the external image as a stage, which keeps the version in one place and makes it overridable: ``` ARG TOOLS_TAG=1.4 FROM ghcr.io/org/tools:${TOOLS_TAG} AS tools ... COPY --from=tools /usr/local/bin/migrate /usr/local/bin/ ``` ## Path resolution Source paths are interpreted inside the source stage's or image's root filesystem. `WORKDIR` in your *current* stage affects only the destination; the source path is not relative to it. Relative source paths are resolved against the source stage's root, so write absolute paths and remove the ambiguity. Wildcards work, and directory-copy semantics are the same as ordinary `COPY`: `COPY --from=build /src/dist/ /app/` copies the *contents* of `dist`. Ownership is not inherited from the source stage's file metadata in a way you should rely on — combine with `--chown=uid:gid` when the runtime stage runs as a non-root user, since numeric IDs are what matters (the source stage's `/etc/passwd` names mean nothing in the destination). ## The stage graph BuildKit parses the whole Dockerfile into a dependency graph before running anything. `COPY --from=X` creates an edge from the current stage to X. Consequences worth stating in an interview: - **Any earlier stage is reachable**, not only your ancestor. A `test` stage and a `prod` stage can both copy from a shared `deps` stage. - **Unreferenced stages are skipped.** If you `--target prod` and the `test` stage is not in `prod`'s dependency closure, it is never built. This is why a test stage does not automatically gate a production build — you must build it explicitly or create a dependency on it. - **Independent stages run in parallel**, since the graph, not the file order, drives execution. - **Only the copied paths matter** for cache purposes on the consuming side: if the builder stage rebuilds but produces a byte-identical artifact, the consuming `COPY --from` can still hit cache. ## Related flags `COPY --from` combines with the other COPY flags: `--chown` and `--chmod` for ownership and permissions in the destination, and `--link` to write the copied content as an independent layer that does not depend on the previous layers' state. `--link` is particularly natural with `--from`, because artifacts pulled from a builder are exactly the kind of self-contained content that benefits from being rebased rather than re-copied when earlier layers change. One restriction to remember: `--from` is a `COPY` flag. `ADD` does not offer it, so cross-stage copying is always a `COPY`.

  • If you copy a file out of `nginx:1.27` with COPY --from, does your image inherit nginx's ENTRYPOINT or its environment variables?
    No. `--from` with an image reference is purely a filesystem read: the image is pulled so its files can be accessed, and only the bytes you copy land in a new layer of your image. Configuration metadata — ENV, ENTRYPOINT, CMD, USER, EXPOSE, labels — belongs to that image's config and is not merged into yours. Only `FROM` inherits metadata.
  • Why is `--from=0` considered a hazard in a Dockerfile that several people maintain?
    Indexes are positional. Inserting a new stage anywhere before the referenced one silently shifts every index, so the COPY starts reading from a different filesystem without any error at parse time. The build may even succeed and produce a wrong image. Naming stages with `AS` makes the reference stable and self-documenting.

saying these in an interview costs you the question

  • Thinking `COPY --from=someimage` also brings that image's ENTRYPOINT or ENV
  • Believing you can only copy from the immediately preceding stage
  • Assuming the source path is relative to the current stage's WORKDIR
  • Expecting a stage that nothing references to still be built and its RUN steps to run
  • Trying to use `--from` on ADD

context