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.

How to Copy Multiple Files in One Layer Using a Dockerfile

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 COPY lines.
  • Fewer layers (sometimes): Each COPY creates 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:

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 COPY changes, the cache for that whole COPY layer 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

PatternDescriptionExample
*Matches any characters*.txt copies all text files
?Matches single characterfile?.txt matches file1.txt, fileA.txt
**Not reliably supported for recursive matching in Dockerfile globsPrefer 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

  1. Group related files: Keep similar files in the same directory
  2. Use descriptive names: Directory names should clearly indicate their purpose
  3. Avoid deep nesting: Keep directory structure simple and shallow
  4. 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/
# 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.