What's the difference between COPY and ADD?
Quick Answer
COPY does exactly one thing: copies files/directories from the build context into the image — simple, predictable, and the generally recommended default. ADD does everything COPY does, plus two extra behaviors: it can automatically extract local .tar archives into the destination, and it can fetch a remote URL directly into the image. Docker's own official guidance recommends COPY unless you specifically need one of ADD's extra behaviors, since ADD's implicit magic (especially auto-extraction) has surprised people in ways that caused real bugs.
Detailed Answer
COPY — simple, explicit, predictable
COPY package.json package-lock.json ./
COPY src/ ./src/
Copies files or directories from the build context straight into the image's filesystem, with no additional behavior — what you see is exactly what happens.
ADD — COPY, plus automatic extraction and remote URL fetching
# ADD automatically extracts a LOCAL tar archive into the destination
ADD myapp.tar.gz /app/
# ADD can fetch directly from a URL
ADD https://example.com/config.json /app/config.json
The first example is the behavior that most often surprises people. If the source is a recognized local archive format (.tar, .tar.gz, .tar.bz2, etc.), ADD automatically unpacks it into the destination directory. COPY would instead just copy the compressed archive file itself, unextracted. This implicit "maybe it extracts, maybe it doesn't, depending on file type" behavior is exactly what Docker's own documentation calls out as a source of confusion.
Why COPY is the recommended default
- Predictability —
COPY's behavior is a single, simple operation with no hidden conditional logic based on file type. ADD's remote-URL fetching is generally discouraged — fetching a remote file directly in a Dockerfile instruction means the build result depends on the state of a URL outside your control at build time, which is worse for reproducibility. The fetched file also isn't automatically cleaned up if it's only needed transiently. ARUN curl ... && ...in the same layer, or a multi-stage build, gives more explicit control over this.ADD's auto-extraction is only useful in one specific scenario: unpacking a local tarball as part of assembling the image. This is legitimate, but narrow enough that it's worth reaching forADDdeliberately for that one purpose, rather than defaulting to it out of habit. A remote URL fetch in particular is better done as an explicitRUN curl/wget, or a multi-stage build step that verifies the artifact, than viaADD's implicit behavior.