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 you build Docker images, you’ll often need to copy multiple files at once. Docker’s COPY instruction lets you specify multiple sources in a single command, which creates one filesystem layer for all of them.

The basic syntax looks like this:

COPY file1.txt file2.txt /app/files/

This copies both file1.txt and file2.txt from your build context into the /app/files/ directory in your image.

Why does this matter?

  • Grouping files that change together keeps your cache from breaking unnecessarily
  • Fewer COPY lines make your Dockerfile cleaner
  • Each COPY creates its own layer, so fewer instructions means fewer layers (though the real benefit here is caching, not just layer count)

Some other Docker articles you might find useful:

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 whatever context you provide) into the image filesystem.

One thing to keep in mind: COPY can’t read files outside the build context. If you need that, restructure your 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)

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:

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 might 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/

Some things to know:

  • When you provide multiple sources, the destination should be a directory (ending with / is a good convention)
  • This is where you want to group “files that change together” (configs together, scripts together, that sort of thing)
  • If one file in a grouped COPY changes, the cache for that whole layer gets invalidated

Using wildcards with COPY (and their limitations)

Wildcards help you copy multiple files matching a pattern, but it’s worth knowing what Dockerfile globs can and can’t 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 isn't guaranteed in Dockerfile globs
# Either list files explicitly:
COPY config.json config.yml config.yaml /app/config/
# Or copy the whole directory:
# COPY config/ /app/config/

The thing is, Dockerfile wildcard behavior isn’t the same as your shell. Avoid fancy patterns. When in doubt, COPY the directory and use .dockerignore to exclude what you don’t want.

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 in the same directory
  2. Use names that clearly describe what’s inside
  3. Avoid deep nesting - keep it simple
  4. Add 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

Use .dockerignore (this one’s important)

.dockerignore reduces build context size, speeds up builds, and keeps you from accidentally copying secrets and junk into your image.

Example .dockerignore:

node_modules/
*.log
.git/
.DS_Store
.env
.env.*

Order COPY steps by change frequency

Copy dependency manifests first, install dependencies, then copy the rest. That way, code changes don’t break your 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/

Be careful with wildcards

Wildcards are handy, but they can:

  • match extra files by accident
  • break your cache more than you’d expect

Use explicit copies for critical cache 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 help reduce build time and keep your Docker image rebuilds predictable.

Conclusion

Copying multiple files in one COPY instruction is straightforward and keeps Dockerfiles clean. The biggest optimization isn’t fancy wildcards - it’s keeping your build context small and ordering your files so caching works well.

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 points

  • Use single COPY instructions for related files
  • Wildcards work for pattern matching
  • Order files by how often they change (better caching)
  • Use .dockerignore to exclude unnecessary files
  • Keep your directory structure organized

Practice with different patterns and you’ll find what works for your setup.