Suggest an editImprove this articleRefine the answer for “COPY vs ADD in Dockerfile: what is the difference?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`COPY` and `ADD`** both copy files from the build context into the image, but `ADD` does extra magic: it auto-extracts local tar archives and can fetch from remote URLs. For plain file copies, prefer `COPY`. ```dockerfile COPY package.json /app/ # plain copy, predictable ADD app.tar.gz /app/ # also extracts the tarball ADD https://example.com/x.deb / # also downloads from URL (avoid) ``` **Key:** use `COPY` by default. Only reach for `ADD` when you specifically need tar extraction or remote URL fetching. The Dockerfile best-practices guide makes this rule explicit.Shown above the full answer for quick recall.Answer (EN)Image**`COPY` and `ADD`** in Dockerfile look almost identical at first glance — both copy files from your build context into the image. The difference is what `ADD` does *on top of* copying. ## Theory ### TL;DR - `COPY` = plain file/directory copy. Predictable. **Default choice.** - `ADD` = `COPY` + auto-extract local tar archives + fetch from remote URLs. - Both support `--chown`, `--chmod`, `--from=stage` flags identically. - The Docker Dockerfile best-practices guide says: "Although `ADD` and `COPY` are functionally similar, generally speaking, `COPY` is preferred." - Reach for `ADD` only when you specifically need its tar or URL feature. ### Quick example ```dockerfile FROM alpine:3.21 # COPY: plain copy. What you see is what you get. COPY package.json /app/ COPY src/ /app/src/ # ADD: copy + extract tar (tar.gz, tar.bz2, tar.xz, tar) ADD app.tar.gz /app/ # ↑ The tarball is unpacked in /app/, NOT placed there as-is. # ADD: also fetches URLs (works, but discouraged) ADD https://example.com/file.deb /tmp/ # ↑ No checksum check, no caching, no retry control. ``` Four lines, two surprises if you did not know `ADD`'s extras. ### Comparison table | Feature | `COPY` | `ADD` | |---|---|---| | Local file/dir copy | Yes | Yes | | `--chown=user:group` flag | Yes | Yes | | `--chmod=755` flag | Yes | Yes | | `--from=stage` (multi-stage) | Yes | Yes | | Auto-extract local tar | No | **Yes** | | Fetch from URL | No | **Yes** | | Predictable, easy to read | **Yes** | No (depends on input) | | **Recommended default** | **Yes** | No | ### When `ADD` is actually the right call - You have a local tarball you want unpacked in one step. Saves a `RUN tar xf ...` line. - You are building from scratch and need to pull a base rootfs as a tar (`FROM scratch` + `ADD rootfs.tar.gz /`). For URL fetches, **prefer a `RUN curl ... && check-checksum && rm`** in a single `RUN`. You get explicit control over verification and can clean up in the same layer. ### Common mistakes **Using `ADD` for plain copies because it sounds more powerful** ```dockerfile # WRONG: surprises future readers ADD package.json /app/ # RIGHT: plain copy with plain command COPY package.json /app/ ``` `ADD` here is functionally the same as `COPY`, but a teammate reading the file has to wonder "is this file a tarball that gets extracted?" Reduce cognitive load. Use `COPY`. **Using `ADD <URL>` and trusting it for production** ```dockerfile # WRONG: no checksum verification, no retry, leaves garbage ADD https://example.com/binary.deb /tmp/ RUN dpkg -i /tmp/binary.deb # RIGHT: explicit fetch with verification RUN curl -fsSL -o /tmp/binary.deb https://example.com/binary.deb && \ echo "<sha256> /tmp/binary.deb" | sha256sum -c - && \ dpkg -i /tmp/binary.deb && \ rm /tmp/binary.deb ``` The `RUN` version is verifiable, cleans up in the same layer, and fails loudly if the upstream binary changes silently. **Forgetting that `ADD some.tar.gz /` extracts** ```dockerfile # Surprise: this does NOT put a tarball in /opt/ ADD vendor.tar.gz /opt/ # It extracts the contents into /opt/. # If you wanted the file as-is, COPY it. ``` A classic gotcha when migrating Dockerfiles. ### Real-world usage - **Most production Dockerfiles** use `COPY` exclusively. The `ADD` lines you see are usually `ADD some-rootfs.tar.gz /` in `FROM scratch` minimal images. - **Distroless and Alpine base images** themselves are built with `ADD <rootfs>.tar.gz /` because that is exactly what `ADD` was designed for. - **Google Cloud Build, GitHub Actions Dockerfile linters** flag `ADD <URL>` and recommend the `RUN curl` pattern. ### Follow-up questions **Q:** If `ADD` does more, why is it not the default recommendation? **A:** Because "more" means "less predictable". Reading `COPY foo /bar`, you know exactly what happens. Reading `ADD foo /bar`, you have to know the type of `foo` to know what happens. Boring and explicit beats clever. **Q:** Does `ADD` extract `.zip`? **A:** No. Only tar variants: `.tar`, `.tar.gz` (or `.tgz`), `.tar.bz2`, `.tar.xz`. For `.zip`, use `COPY` plus a `RUN unzip`. **Q:** Can I use `COPY` with a URL? **A:** Not directly. With BuildKit (`# syntax=docker/dockerfile:1.7+`), you can use `ADD <URL>` with checksum verification via `--checksum=sha256:...` — that is the modern, safe way to fetch URLs. Plain `COPY` remains local-only. **Q:** (Senior) When would you intentionally pick `ADD` for a URL today? **A:** With BuildKit's `ADD --checksum=sha256:abc... https://example.com/file`, which gives you a verifiable, cache-friendly download in one line. That is the only `ADD <URL>` pattern I would put in production. ## Examples ### The 99 percent case: `COPY` for everything ```dockerfile FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . USER node CMD ["node", "server.js"] ``` No `ADD`. No surprises. Clear cache boundaries. This is what most production Dockerfiles look like. ### Legitimate `ADD`: building a base image from a rootfs ```dockerfile FROM scratch ADD alpine-minirootfs-3.21.0-x86_64.tar.gz / CMD ["/bin/sh"] ``` Here `ADD` does its real job: unpack a tar into the image. This is exactly how the official `alpine` image is built upstream.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.