1. Basic Syntax#
The syntax is almost identical, but the behavior is very different:
1
2
3
4
5
|
# COPY - pure file copy, no extra processing
COPY [--chown=<user>:<group>] <src>... <dest>
# ADD - supports auto-extraction and remote URLs, but with unpredictable behavior
ADD [--chown=<user>:<group>] <src>... <dest>
|
ADD automatically extracts local archives, while COPY does not:
1
2
3
4
5
6
7
|
# ADD auto-extracts .tar.gz
ADD app.tar.gz /opt/app/
# Result: contents are extracted into /opt/app/
# COPY only copies the file itself
COPY app.tar.gz /opt/app/
# Result: /opt/app/app.tar.gz remains an archive
|
Supported archive formats: .tar, .tar.gz / .tgz, .tar.bz2, .tar.xz
If you want to copy an archive without extracting it, you must use COPY.
3. Remote URL Downloads#
ADD can download files directly from a URL; COPY does not:
1
2
3
4
5
|
# ADD can fetch a remote file (not recommended)
ADD https://example.com/app.tar.gz /opt/
# COPY does not support URLs
COPY https://example.com/app.tar.gz /opt/ # Error!
|
Prefer RUN + curl over ADD for remote downloads: ADD downloads don’t benefit from build cache, can’t handle errors, and don’t verify integrity.
1
2
3
4
|
# Recommended: RUN + curl
RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \
&& tar -xzf /tmp/app.tar.gz -C /opt/ \
&& rm /tmp/app.tar.gz
|
4. Auto-Creating Directories#
Both automatically create missing intermediate directories:
1
2
3
4
5
6
|
# If /etc/myapp/config/ doesn't exist, it will be created automatically
COPY app.conf /etc/myapp/config/
ADD app.conf /etc/myapp/config/
# No need for manual mkdir
# RUN mkdir -p /etc/myapp/config ← unnecessary
|
5. Build Cache Behavior#
1
2
3
4
5
|
# COPY - strictly matches file content; cache is only invalidated when content changes
COPY config/app.conf /etc/app/
# ADD - cache stays valid as long as the archive content is the same
ADD app.tar.gz /opt/app/
|
| Scenario |
COPY |
ADD |
| File content changes |
Cache invalidated |
Cache invalidated |
| File metadata changes |
Cache invalidated |
Cache invalidated |
| Same archive content |
N/A |
Cache valid |
6. Feature Comparison#
| Feature |
COPY |
ADD |
| Copy local files |
✅ |
✅ |
| Copy directories |
✅ |
✅ |
| Wildcard support |
✅ |
✅ |
| Auto-create directories |
✅ |
✅ |
| Auto-extract archives |
❌ |
✅ |
| Remote URL support |
❌ |
✅ |
| Clarity of intent |
✅ High |
❌ Low |
| Recommended |
✅ Default |
⚠️ Specific cases |
7. When to Use Each#
Use COPY for:
1
2
3
4
5
6
7
8
9
|
# Copy config files
COPY nginx.conf /etc/nginx/nginx.conf
# Copy application code
COPY src/ /app/src/
COPY package.json /app/
# Copy a binary from a multi-stage build
COPY --from=builder /app/build/app /usr/local/bin/
|
Use ADD only when auto-extraction is needed:
1
2
3
4
5
|
# Reasonable use of ADD
ADD node-v18.17.0-linux-x64.tar.gz /usr/local/
# Don't use ADD for regular files
ADD app.conf /etc/app/ # ← use COPY instead
|
8. Best Practices#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# Rule 1: default to COPY
COPY requirements.txt /app/ # ✅
ADD requirements.txt /app/ # ❌ unnecessary
# Rule 2: use ADD only when extraction is needed
ADD node-v18.tar.gz /usr/local/ # ✅ extraction needed
# Rule 3: use RUN + curl for remote files
RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \
&& tar -xzf /tmp/app.tar.gz -C /opt/ \
&& rm /tmp/app.tar.gz # ✅
ADD https://example.com/app.tar.gz /opt/ # ❌
# Rule 4: use .dockerignore to shrink the build context
# .dockerignore
# .git
# node_modules
# *.md
# .env
# Rule 5: order COPY statements to leverage cache
COPY package.json package-lock.json ./ # dependencies first (rarely change)
RUN npm ci --production
COPY . . # source code last (changes often)
|
9. Common Pitfalls#
1
2
3
4
5
6
7
8
9
10
|
# Pitfall 1: unexpected auto-extraction
ADD app.tar.gz /opt/app/
# You expected a file copy, but got an extracted directory
# Pitfall 2: remote files can't be cached
ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/
# Every build re-downloads
# Pitfall 3: ownership issues
COPY --chown=app:app src/ /app/src/ # specify the owner explicitly
|
10. Decision Tree#
1
2
3
4
5
6
7
|
Need to copy a file?
├── Local archive that needs extraction?
│ └── Use ADD
├── Local regular file?
│ └── Use COPY
└── Remote file?
└── Use RUN + curl
|