Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 8

Dockerfile: COPY vs ADD

Recommended resources

Hello! Welcome to the final lesson in our module on "Production-Grade Dockerfiles."

In our previous lesson, we explored the importance of signal handling and using an init system like tini to create robust containers that shut down gracefully. This was a crucial step in ensuring your containerized applications are stable and reliable in production.

Today, we'll wrap up this module by examining two of the most frequently used, and often confused, instructions in a Dockerfile: COPY and ADD. Your goal for this lesson is to learn how to use them appropriately based on specific use cases. While they might seem interchangeable at first glance, understanding their distinct functionalities is key to building secure, predictable, and maintainable Docker images.

As a DevOps Engineer, you know that clarity and predictability in your build process are paramount. This lesson will equip you with the knowledge to make deliberate choices between COPY and ADD, avoiding common pitfalls and adhering to best practices.

1. The Core Functionality: What Do COPY and ADD Do?

At a basic level, both instructions serve to get files from your local machine into your Docker image.

  • COPY: This instruction has one primary purpose: to copy files or directories from a source on the host machine (the build context) to a destination in the container's filesystem. Its syntax is straightforward: COPY <src>... <dest>.
  • ADD: This instruction is a superset of COPY. It can do everything COPY does, but it has two additional, "magical" features:
    1. URL Fetching: If the <src> is a URL, ADD will download the file and copy it to the <dest>.
    2. Automatic Extraction: If the <src> is a local tar archive (in a recognized format like gzip, bzip2, or xz), ADD will automatically unpack it into the destination directory.

To see a basic demonstration of these two commands in action, you can watch this short video.

Dockerfile creation Tutorial - Dockerfile Instructions Explained with example!

The following video from Cloud Champ provides a clear, practical demonstration of how COPY works and then introduces the extra capabilities of ADD, including fetching a file from a remote S3 bucket.

Watch the segment from 17:59 to 20:32. First, you'll see a standard COPY operation. Then, the video demonstrates ADD's ability to download a file from a URL.


2. A Side-by-Side Comparison

Given that ADD can do more, a natural question is, "Why not just use ADD all the time?" The answer lies in predictability and transparency. The "magic" features of ADD can sometimes lead to unexpected behavior.

This table provides a great visual summary of the key differences:

Docker ADD vs. COPY Comparison
This table clearly outlines the differences between `COPY` and `ADD` across purpose, functionality, and common use cases, highlighting why `COPY` is generally the preferred option.

To summarize the key points from the table and our discussion so far:

  • COPY is transparent. It only copies local files. There's no ambiguity.
  • ADD has added complexity. Its behavior changes depending on whether the source is a local file, a local archive, or a remote URL.

3. The Best Practice: Prefer COPY

The official Docker recommendation and widely accepted best practice is to always prefer COPY over ADD unless you specifically need ADD's automatic tar extraction feature.

Let's dig into why this is the recommended approach.

Building best practices: ADD or COPY

The official Docker documentation provides the definitive guide on this topic. It explains why COPY is more transparent and when ADD might still be appropriate.

Please read the section titled 'ADD or COPY'. Pay close attention to the reasoning for using COPY in most scenarios and the specific, narrow use case presented for ADD.

Why COPY is More Transparent and Predictable

As the documentation suggests, transparency is the main driver. With COPY, you know exactly what will happen: files are copied. With ADD, the outcome depends on the file's content.

For example, ADD my-archive.tar.gz /app/ might:

  1. Copy the file my-archive.tar.gz into /app/.
  2. Or, extract the contents of my-archive.tar.gz into /app/.

Docker decides this based on the file's content, not its name. If the archive is corrupted or in an unrecognized format, Docker will silently copy the file instead of extracting it, which can lead to hard-to-debug build failures down the line.

Shall I Use ADD or COPY in the Dockerfile—What's the Difference?

This article from Gcore provides a great explanation of this unpredictability and reinforces the best practices.

Read the sections 'Why COPY is preferred' and 'The best practice'. The author clearly explains how ADD's behavior can make your build pipeline unpredictable.

The Security and Caching Pitfalls of ADD with URLs

While using ADD to download files from a URL seems convenient, it's generally discouraged for two main reasons:

  1. Security: You are adding a file from an external source directly into your image layer without verification. If the remote source is compromised, you could be introducing malware into your image. While ADD does support a --checksum flag, it's often overlooked.
  2. Image Bloat & Cache Inefficiency: You cannot delete the downloaded file in the same layer. A more efficient method is to download, extract, and clean up all within a single RUN instruction. This creates a smaller final image layer.

The recommended alternative is to use RUN with tools like curl or wget. This approach is more explicit, secure, and efficient.

Here's the recommended pattern:

RUN apt-get update && apt-get install -y wget && \
    wget -O /tmp/my-tool.tar.gz https://example.com/my-tool-v1.2.tar.gz && \
    # Optional but recommended: verify checksum
    echo "CHECKSUM_HERE /tmp/my-tool.tar.gz" | sha256sum -c - && \
    tar -xvf /tmp/my-tool.tar.gz -C /usr/local/bin && \
    rm /tmp/my-tool.tar.gz && \
    # Clean up the package manager cache
    rm -rf /var/lib/apt/lists/*

This multi-step RUN command gives you full control, allows for verification, and cleans up after itself, all within a single image layer.


4. So, When Should You Use ADD?

The primary valid use case for ADD is its original intention: to add a local tar file from the build context and have it automatically extracted into the image. This is common when you need to unpack a pre-built application or toolchain into your image.

For example: ADD my-app-build.tar.gz /opt/

Even here, you could use COPY my-app-build.tar.gz /tmp/ followed by a RUN tar -xvf /tmp/my-app-build.tar.gz ..., but using ADD is more concise for this specific task.

Test your understanding!

You are writing a Dockerfile for a Python application. You need to perform the following steps:

  1. Copy your local application source code from the ./src directory to /app in the image.
  2. Download the tini static binary from https://github.com/krallin/tini/releases/download/v0.19.0/tini-static and place it at /usr/local/bin/tini.

Based on best practices, which instructions (COPY, ADD, or RUN) would you use for each step? Write the two corresponding Dockerfile lines.

Show answer

Based on best practices, you should use COPY for the local source code and RUN with a download tool for the remote binary.

# Step 1: Use COPY for local files. It's transparent and predictable.
COPY ./src /app

# Step 2: Use RUN with curl/wget for remote files. This is more secure and explicit.
# It also allows for setting permissions in the same layer.
RUN curl -L -o /usr/local/bin/tini https://github.com/krallin/tini/releases/download/v0.19.0/tini-static && \
    chmod +x /usr/local/bin/tini

Using ADD for the second step (ADD https://... /usr/local/bin/tini) would work, but it's less transparent and considered a less robust practice than using RUN with curl or wget.


Conclusion

Congratulations on completing the "Production-Grade Dockerfiles" module! You've covered a lot of ground, from optimizing build caches and creating lean multi-stage builds to securing your containers and ensuring they run robustly.

Today, you learned the final piece of this puzzle: the subtle but important difference between COPY and ADD.

Key Takeaways:

  • COPY is for one thing: copying files and directories from the build context. It is transparent and predictable.
  • ADD can also copy local files, but adds functionality for downloading from URLs and auto-extracting local tar archives.
  • Best Practice: Always default to COPY. Its simplicity makes your Dockerfiles easier to read, understand, and maintain.
  • Use ADD only when you specifically need its auto-extraction feature for a local tarball.
  • For downloading remote files, the most robust and secure pattern is to use RUN with curl or wget, which allows for checksum verification and cleanup in a single layer.

You are now equipped with the foundational skills to write Dockerfiles that are not just functional but also efficient, secure, and ready for production environments.

In our next module, "Advanced Image and Runtime Management," we'll take the production-ready image you now know how to build and learn how to tag it effectively and push it to a container registry like Docker Hub, making it available for deployment.