How to Copy Multiple Files in One Layer Using a Dockerfile
Learn how Dockerfile COPY handles multiple sources and wildcards, what it can’t do, and how to optimize builds with .dockerignore and BuildKit.
When building Docker images, you’ll often need to copy multiple files into your image. Docker’s COPY instruction supports multiple sources in a single instruction, which results in a single filesystem layer created by that instruction.
The basic syntax is:
COPY file1.txt file2.txt /app/files/
This copies both file1.txt and file2.txt from your build context into the /app/files/ directory inside the image.
Why this matters:
- Better cache behavior: Group files that change together to avoid invalidating unrelated layers.
- Cleaner Dockerfiles: Fewer repetitive
COPYlines. - Fewer layers (sometimes): Each
COPYcreates its own layer, so fewer instructions can reduce layer count (though the main win is usually caching strategy, not layer count alone).
Some other docker articles that can help you in your docker journey:
- Add Users to a Docker Container
- Install Docker & Docker-compose for Ubuntu ARM
- Redirect Docker Logs to a Single File
- Environment Variables ARG and ENV in Docker
Using the COPY instruction to copy files in a Dockerfile
COPY transfers files from your build context (the directory you run docker build from, or the context you provide) into the image filesystem.
Important: COPY can’t read files outside the build context. If you need that, restructure the context or use CI to assemble one.
Basic syntax
COPY <src> <dest>
<src>: file(s) or directory in the build context<dest>: destination path in the image (directory must exist or Docker will create it as needed)
Common Use Cases
Copy a single file:
COPY app.js /app/
Copy an entire directory:
COPY src/ /app/src/
Copy multiple specific files:
COPY file1.txt file2.txt config.json /app/
Copy files with wildcards (shell-style globs):
COPY *.txt /app/
This copies matching .txt files from the build context directory into /app/.
Note: patterns are evaluated by Docker (not your shell) and don’t support every “bash glob” feature you may expect.
Including multiple source files in a single COPY instruction
You can list multiple sources and copy them into a directory destination:
COPY file1.txt file2.txt config.json /app/
COPY *.csv /data/
COPY src/ /code/
Notes:
- When you provide multiple sources, the destination should be a directory (ending with
/is a good convention). - This is a great place to group “files that change together” (for example: configs together, scripts together).
- If one file in a grouped
COPYchanges, the cache for that wholeCOPYlayer is invalidated.
Using wildcards with COPY (and their limitations)
Wildcards (globs) help you copy multiple files matching a pattern, but it’s important to know what Dockerfile globs can and cannot do.
Common Wildcard Patterns
| Pattern | Description | Example |
|---|---|---|
* | Matches any characters | *.txt copies all text files |
? | Matches single character | file?.txt matches file1.txt, fileA.txt |
** | Not reliably supported for recursive matching in Dockerfile globs | Prefer copying a directory and controlling contents with .dockerignore |
Practical Examples
# Copy all configuration files from the context root
COPY *.conf /etc/app/
# Prefer copying a directory, then excluding unwanted files via .dockerignore
COPY src/ /app/src/
# Brace expansion like *.{json,yml,yaml} is a shell feature and is not guaranteed in Dockerfile globs
# Prefer either explicit files:
COPY config.json config.yml config.yaml /app/config/
# Or copy the whole directory:
# COPY config/ /app/config/
Important: Dockerfile wildcard behavior is not the same as your shell. Avoid fancy patterns. When in doubt, COPY the directory and use .dockerignore to exclude what you don’t want in the build context.
Organizing files in separate directories for better readability
Organizing Files for Better Dockerfile Management
A well-organized directory structure makes your Dockerfile easier to maintain:
project/
├── src/ # Application source code
├── config/ # Configuration files
├── scripts/ # Build and deployment scripts
├── assets/ # Static assets
└── Dockerfile
Best Practices
- Group related files: Keep similar files in the same directory
- Use descriptive names: Directory names should clearly indicate their purpose
- Avoid deep nesting: Keep directory structure simple and shallow
- Document structure: Include a README explaining your organization
Example Dockerfile with organized structure:
# Copy source code
COPY src/ /app/src/
# Copy configuration files
COPY config/*.json /app/config/
# Copy build scripts
COPY scripts/build.sh /app/scripts/
Best practices for efficient file copying in a Dockerfile
1) Use .dockerignore (biggest win)
.dockerignore reduces build context size, speeds up builds, and prevents accidentally copying secrets and junk into the image.
Example .dockerignore:
node_modules/
*.log
.git/
.DS_Store
.env
.env.*
2) Order COPY steps by change frequency (cache-friendly)
Copy dependency manifests first, install dependencies, then copy the rest. That way, code changes don’t invalidate dependency layers.
WORKDIR /app
# Dependencies (change less often)
COPY package.json package-lock.json ./
RUN npm ci
# App source (changes often)
COPY src/ ./src/
3. Group Related Files
# Copy all config files together
COPY config/ /app/config/
# Copy all static assets together
COPY assets/ /app/assets/
4) Be careful with wildcards
Wildcards are convenient, but they can:
- accidentally match extra files
- invalidate cache more often than you expect
Prefer explicit copies for critical “cache key” files (like dependency manifests), and use .dockerignore to keep the context clean.
# Good when you control the directory contents (and .dockerignore is set)
COPY config/ /app/config/
# Best for dependency caching (explicit, stable)
COPY package.json package-lock.json /app/
These practices reduce build time and help you keep Docker image rebuilds predictable.
Conclusion
Copying multiple files in one COPY instruction is a simple technique that keeps Dockerfiles cleaner and helps you control caching. The most important “modern” optimization is usually not fancy wildcards—it’s small build contexts and cache-friendly ordering.
Quick Reference
# Multiple specific files into a directory
COPY file1.txt file2.txt config.json /app/
# Simple wildcard at context root
COPY *.conf /etc/app/
# Prefer copying directories + .dockerignore over recursive glob tricks
COPY src/ /app/src/
COPY config/ /app/config/
Key Takeaways
- Use single COPY instructions for multiple related files
- Leverage wildcards for pattern matching
- Order files by change frequency for better caching
- Use .dockerignore to exclude unnecessary files
- Keep your directory structure organized
Mastering these file copying techniques will make your Docker builds faster and more efficient. Practice with different patterns to find what works best for your specific use case.