Skip to content
Go back

Dockerfile: Differences Between COPY and ADD

· Updated:
By SumGuy 5 min read
Dockerfile: Differences Between COPY and ADD

When building Docker images, the Dockerfile serves as a blueprint for constructing the image. It contains a set of instructions that Docker follows to build the image. Among these instructions, the COPY and ADD commands are often used to include files from the local file system into the Docker image. Although they seem similar, there are distinct differences in their functionality and use cases.

What is a Dockerfile?

Before diving into the specifics of COPY and ADD, it’s important to understand what a Dockerfile is. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

The COPY Command

The COPY command in a Dockerfile copies new files or directories from a source and adds them to the filesystem of the container at the specified destination. It is straightforward and explicitly designed to copy local files.

Syntax:

COPY [--chown=<user>:<group>] <source>... <destination>

Example:

COPY ./app /usr/src/app

This line in a Dockerfile would copy the local directory ./app into the /usr/src/app directory inside the Docker container.

Key Points for COPY:

The ADD Command

The ADD command is more complex and powerful. It copies files and directories like COPY, but it also has the capability to handle URL sources and automatically extract compressed files.

Syntax:

ADD [--chown=<user>:<group>] <source>... <destination>

Example:

ADD https://example.com/example.tar.gz /var/www/html

This command would download the example.tar.gz file from the specified URL and extract it into /var/www/html inside the container.

Key Points for ADD:

When to Use COPY vs. ADD

Best Practices

Conclusion

Understanding the differences between COPY and ADD in Dockerfiles helps in making better decisions when writing Dockerfiles. While both commands have their place, choosing the right command based on the requirement can simplify Docker image creation and make your Dockerfiles more efficient and secure.

Gotchas That’ll Bite You at 2 AM

Knowing the rules is one thing. Getting burned by the edge cases is how you actually internalize them.

The silent tarball extraction trap. Say you’re packaging your app and you genuinely want to ship a .tar.gz as a file — not extracted, just sitting there for something downstream to deal with. Use ADD and it’ll silently unpack it. Your container runs, the file isn’t where you expected, and you spend 20 minutes docker exec-ing around wondering what happened. COPY respects the archive as a file. ADD does not.

Cache invalidation with ADD + URLs. Docker’s layer cache is smart about local files — it hashes them and only rebuilds if content changes. URLs? It can’t do that. Every time Docker processes ADD https://example.com/something.tar.gz /dest, it has to assume the remote content might have changed. Depending on your Docker version and build flags, you may get stale content or forced rebuilds at the worst possible moment. The more predictable move is RUN curl or RUN wget with an explicit version pinned in the URL.

Terminal window
# This looks clean but you'll fight cache invalidation forever
ADD https://releases.example.com/tool-latest.tar.gz /usr/local/
# This is more verbose but you control exactly what you get
RUN curl -fsSL https://releases.example.com/tool-1.4.2.tar.gz \
| tar -xz -C /usr/local/ \
&& rm -rf /tmp/build

--chown doesn’t apply to URL sources. If you’re using ADD with a URL and you think --chown=app:app is setting ownership on the downloaded file — it’s not. The flag works fine for local files and archives, but URL sources ignore it. You’ll need a RUN chown after the fact, which is an extra layer anyway. At that point, just use RUN curl and be done with it.

Permissions on extracted archives. When ADD unpacks a tarball, it preserves whatever permissions were baked into the archive. That might be root:root and 755, or it might be some nonsense from whoever created the archive. Don’t assume. Always verify with a quick docker run --rm <image> ls -la /your/path after a build if you’re relying on specific permissions.

The pattern that avoids most of these headaches: use COPY for everything you own and control, reach for RUN curl | tar when you need remote archives with a pinned version, and treat ADD as a specialized tool you deploy deliberately — not the default when COPY feels like too much typing.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Docker Volume Mounts: Essential Flags
Next Post
Enable WebGL on Chrome or Firefox

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts