DockerfileのCOPYとADD、どちらを使うべき?
Dockerfileでホストのファイルをコンテナに配置する命令として、COPYとADDの2つがあります。どちらもファイルをコピーする命令ですが、実は挙動に明確な違いがあります。
「どちらを使えばいいのか分からない」「ADDの方が高機能だからADDでいいのでは?」と迷っている方に向けて、この記事では両者の違いと使い分けの基準をわかりやすく解説します。
結論: 基本はCOPYを使う
先に結論をお伝えすると、基本的にはCOPYを使うべきです。ADDを使うのは、tar.gzファイルを自動展開したい場合のみに限定しましょう。
Docker公式のベストプラクティスでも、以下のように明記されています。
“For other items (files, directories) that do not require ADD’s tar auto-extraction capability, you should always use COPY.”
つまり、tar自動展開が不要な場面では常にCOPYを使うのが推奨されています。
COPYとADDの違い — 比較表
まずは、両者の機能を比較表で確認しましょう。
| 機能 | COPY | ADD |
|---|---|---|
| ローカルファイルのコピー | ✅ 可能 | ✅ 可能 |
| ディレクトリのコピー | ✅ 可能 | ✅ 可能 |
| tar.gzの自動展開 | ❌ 不可 | ✅ 可能(gzip, bzip2, xz対応) |
| URLからのダウンロード | ❌ 不可 | ✅ 可能(ただし非推奨) |
| Docker公式の推奨 | ✅ 推奨 | 限定的な場面のみ |
このように、ADDはCOPYの上位互換のように見えますが、「高機能 = 使うべき」ではありません。その理由は後述します。
COPYの使い方と実例
COPYは、ホストマシンのファイルやディレクトリをコンテナ内にコピーするシンプルな命令です。
基本構文
COPY <コピー元> <コピー先>
実例: アプリケーションファイルをコピーする
FROM python:3.11-slim
WORKDIR /app
# requirements.txtをコピーして依存パッケージをインストール
COPY requirements.txt .
RUN pip install -r requirements.txt
# アプリケーションコードをコピー
COPY src/ ./src/
COPY main.py .
CMD ["python", "main.py"]
COPYは「ファイルをそのままコピーする」だけなので、挙動が明確で予測しやすいのが特徴です。
ディレクトリごとコピーする場合
# configディレクトリをまるごとコピー
COPY config/ /app/config/
💡 COPYのポイント
COPYはローカルのビルドコンテキスト内のファイルのみを対象とします。ビルドコンテキスト外のファイル(例: ../secret.txt)はコピーできません。
ADDの使い方と実例
ADDはCOPYの機能に加えて、tarアーカイブの自動展開とURLからのダウンロードが可能です。
基本構文
ADD <コピー元> <コピー先>
実例: tarアーカイブを自動展開する
ADDの最大の利点は、ローカルのtarアーカイブを自動的に展開してくれることです。
FROM ubuntu:22.04
WORKDIR /opt
# app.tar.gzを/opt/に展開(自動的に解凍・展開される)
ADD app.tar.gz /opt/
上記の場合、app.tar.gzが自動的に解凍・展開され、中身のファイルが/opt/以下に配置されます。COPYで同じことをすると、tar.gzファイルがそのままコピーされるだけで展開はされません。
対応するアーカイブ形式はgzip、bzip2、xzです。
⚠️ URL経由のtarは自動展開されない
ADDでURLからtarファイルをダウンロードした場合、自動展開は行われません。自動展開されるのはローカルのtarファイルのみです。この挙動の違いが混乱を招くため、URLダウンロードにADDを使うことは推奨されていません。
なぜCOPYが推奨されるのか
Docker公式のDockerfileベストプラクティスでCOPYが推奨されている理由は、主に2つあります。
理由1: 挙動が予測しやすい
COPYは「ファイルをそのままコピーする」という単純な挙動しかありません。一方、ADDは渡されたファイルの種類によって挙動が変わります。
- 通常のファイル → そのままコピー
- tar.gzファイル → 自動的に展開
- URL → ダウンロードしてコピー
例えば、意図せずtar.gzファイルをADDで配置してしまうと、想定外の展開が行われる可能性があります。COPYなら常に「そのままコピー」なので、意図しない挙動が起きません。
理由2: セキュリティ上の懸念
ADDのURL機能を使うと、外部からファイルをダウンロードしてイメージに含めることになります。ダウンロード元の改ざんリスクや、ファイル内容の検証が難しいという問題があります。COPYにはこの機能がないため、セキュリティリスクが限定的です。
ADDのURL機能を使うべきでない理由
ADDにはURLからファイルをダウンロードする機能がありますが、これは使うべきではありません。代わりにRUN curlやRUN wgetを使いましょう。
ADDでURLを使った場合の問題
# 非推奨: ADDでURLダウンロード
ADD https://example.com/app.tar.gz /tmp/app.tar.gz
RUN tar -xzf /tmp/app.tar.gz -C /opt/ && rm /tmp/app.tar.gz
ADDでダウンロードすると、そのファイルがイメージレイヤーにキャッシュされます。その後にRUNで削除しても、レイヤーには残り続けるため、イメージサイズが無駄に大きくなります。
推奨: RUN curl/wgetを使う
# 推奨: RUN curlでダウンロード→展開→削除を1レイヤーで完結
RUN curl -fsSL https://example.com/app.tar.gz -o /tmp/app.tar.gz \
&& tar -xzf /tmp/app.tar.gz -C /opt/ \
&& rm /tmp/app.tar.gz
この方法なら、ダウンロード・展開・削除が1つのレイヤーで完結するため、イメージサイズを最小限に抑えられます。
💡 1つのRUNにまとめるのがポイント
ダウンロード・展開・削除を別々のRUN命令にすると、それぞれがレイヤーとして残ります。&&で繋いで1つのRUN命令にまとめることで、不要なファイルを最終レイヤーに残さずに済みます。
まとめ
DockerfileのCOPYとADDの使い分けを整理しました。
- 基本はCOPYを使う — シンプルで挙動が予測しやすく、Docker公式も推奨
- ADDはtar自動展開が必要なときだけ — ローカルのtar.gzを展開配置したい場合に限定
- ADDのURL機能は使わない —
RUN curl/RUN wgetで代替すべき
迷ったらCOPYを選んでおけば間違いありません。Dockerfileのベストプラクティスに沿った書き方を身につけて、保守性の高いDockerfileを目指しましょう。
Dockerの基礎から体系的に学びたい方は、Docker完全攻略ページも参考にしてください。また、イメージとコンテナの違いやDocker主要コマンドの記事もあわせてご覧ください。



コメント