---
title: "How to Copy Multiple Files in One Layer Using a Dockerfile"
description: "Learn how Dockerfile COPY handles multiple sources and wildcards, what it can't do, and how to optimize builds with .dockerignore and BuildKit."
date: 2026-05-04
categories: ["vps"]
tags: ["docker"]
---

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:

```dockerfile
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:

- [Add Users to a Docker Container](https://www.bitdoze.com/add-users-to-docker-container/)
- [Install Docker & Docker-compose for Ubuntu ARM](https://www.bitdoze.com/install-docker-ubuntu-arm/)
- [Redirect Docker Logs to a Single File](https://www.bitdoze.com/redirect-docker-logs-to-a-single-file/)
- [Environment Variables ARG and ENV in Docker](https://www.bitdoze.com/docker-env-vars/)

## 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

```dockerfile
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:**
```bash
COPY app.js /app/
```

**Copy an entire directory:**
```dockerfile
COPY src/ /app/src/
```

**Copy multiple specific files:**
```dockerfile
COPY file1.txt file2.txt config.json /app/
```

**Copy files with wildcards:**
```dockerfile
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:

```dockerfile
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

| 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

```dockerfile
# 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:
```dockerfile
# Copy source code
COPY src/ /app/src/

# Copy configuration files
COPY config/*.json /app/config/

# Copy build scripts
COPY scripts/build.sh /app/scripts/
```

### BuildKit features worth knowing (Docker Engine v25+)

If you're using BuildKit (which is the default now), a couple of useful options:

- **`COPY --link`** — creates the copy as an independent layer that doesn't depend on parent layers. This means changing a base image doesn't invalidate your cached copy step. Use it when you can:
  ```dockerfile
  COPY --link package.json package-lock.json /app/
  ```
- **`COPY --parents`** — preserves the directory structure from the source. Useful for copying files from nested directories:
  ```dockerfile
  COPY --parents src/utils/*.ts src/components/*.ts /app/
  ```
  This creates `/app/src/utils/` and `/app/src/components/` in the image, preserving the paths.

## 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.

```dockerfile
WORKDIR /app

# Dependencies (change less often)
COPY package.json package-lock.json ./
RUN npm ci

# App source (changes often)
COPY src/ ./src/
```

### Group related files
```dockerfile
# 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.

```dockerfile
# 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

```dockerfile
# 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.