
ビルドコンテキストは、docker build がビルダーへ渡す「入力ファイル一式」です。コンテキストが大きかったり毎回少しでも変わったりすると、転送が遅くなるだけでなく COPY/ADD のキャッシュが崩れやすくなります。.dockerignore はこのコンテキストから不要物を除外し、速度改善・キャッシュ安定化・機密混入防止に効きます。
1. ビルドコンテキストとは
1-1. ビルドコンテキスト=ビルダーへ渡す「入力ファイル一式」
ビルドコンテキスト(build context)とは、docker build がビルド処理を行うビルダー(DockerデーモンやBuildKit)へ渡す入力ファイル一式のことです。Dockerfileだけが入力だと思いがちですが、実際のビルドでは「Dockerfile+それ以外のファイル(ソースコード、設定、依存定義など)」を使ってイメージを組み立てます。
この“入力ファイル一式”がビルドコンテキストなので、ここに何が入っているかがビルドの速さと安定性に直結します。逆に言うと、コンテキストが不要なファイルだらけだと、その分だけビルドが遅くなり、キャッシュも崩れやすくなります。
1-2. どこまでが対象?:docker build <context> の <context> 配下
ビルドコンテキストの範囲は、基本的に docker build <context> の <context> 配下です。よくあるのはリポジトリルートで docker build . を実行するパターンで、この場合は「カレントディレクトリ(.)配下のファイル」がコンテキスト候補になります。
つまり、docker build . をしている限り、同じディレクトリ配下にある dist/ や node_modules/、.git/ なども、何もしなければコンテキストに含まれます。Dockerfileで COPY していないファイルでも、コンテキストとしては“送られる可能性がある”点が落とし穴になります(次章で詳しく触れます)。
1-3. なぜ重要?:コンテキストが変わると COPY/ADD のキャッシュが崩れやすい
ビルドコンテキストが重要な理由は2つあります。1つ目は単純に「送る量が増えると遅い」からです。2つ目がより本質で、コンテキストが大きくて変化しやすいと、COPY/ADD のキャッシュが崩れやすくなるからです。
Dockerのキャッシュは「同じ入力なら再利用」ですが、COPY の入力は“コピー対象ファイルの内容”です。コンテキストに「毎回変わるファイル」が混ざっていて、それを COPY . . のように広く取り込んでいると、ちょっとした変化でCOPYの入力が変わった扱いになり、以降のレイヤが全部作り直しになります。.dockerignore はこの変化源をコンテキストから外すために効きます。
2. 何が起きているか:送られる・圧縮される・展開される
2-1. ローカル→ビルダーへファイルが送られる(場合によってはリモートへ)
docker build を実行すると、CLIはコンテキストのファイルをまとめてビルダーへ渡します。Docker Desktopやローカルデーモンなら「ローカルのDockerデーモン」へ送られるだけに見えますが、仕組みとしては「クライアント → ビルダー」への転送が発生しています。
さらに、buildxでリモートbuilder(別ホストやKubernetes上のBuildKit)を使う場合、コンテキストはネットワーク越しに送られます。このときコンテキストが大きいと、転送時間がそのままビルド時間に乗ってきます。「CIだけ遅い」の原因が、実はコンテキスト転送だった、というのはわりとよくあります。
2-2. コンテキストが大きいと遅くなる理由(転送・展開・差分計算・キャッシュ無効化)
コンテキストが大きいと遅くなる理由は、転送だけではありません。一般に、ビルドでは次のようなコストが積み重なります。
- コンテキストの収集(ファイル走査)
- アーカイブ化や圧縮(環境による)
- ビルダー側での展開
COPY対象の差分計算(内容のハッシュ化など)
さらに厄介なのは、「大きくて変わりやすいコンテキスト」はキャッシュを壊しやすい点です。ビルドが遅い原因が「ネットワークが遅い」ではなく、「毎回キャッシュが崩れて依存インストールまでやり直している」ことも多いので、速度改善では“コンテキストのサイズ”と“キャッシュの安定性”をセットで見る必要があります。
2-3. 「一部だけCOPYしてるから大丈夫」の誤解
よくある誤解が「Dockerfileで一部しかCOPYしてないから、余計なファイルがあっても大丈夫」というものです。結論から言うと、半分だけ正しくて、半分危険です。確かに、COPY package.json . のように明示的に限定すれば、その命令の入力は限定されます。
ただし、多くのDockerfileは途中で COPY . . を使いますし、チームの誰かが後から追加することもあります。さらに、余計なファイルがコンテキストにあるだけで「転送量」や「走査コスト」は増えます。つまり、「今のDockerfileが限定COPYだから大丈夫」ではなく、「将来の変更にも耐えるようにコンテキストを小さく安定させる」発想で .dockerignore を整えるのが実務的です。
3. .dockerignore とは(結論と効き方)
3-1. .dockerignore は「コンテキストに含めないファイル」を指定する仕組み
.dockerignore は、Gitの .gitignore に似た仕組みで、「ビルドコンテキストに含めないファイル・ディレクトリ」を指定します。場所は基本的にコンテキストのルートに置きます(docker build . なら ./.dockerignore)。
ここで指定されたパターンにマッチするファイルは、そもそもビルダーへ送られません。つまり、Dockerfileでうっかり COPY . . を書いてしまっても、.dockerignore で除外していれば取り込まれない、という“安全装置”にもなります。
3-2. 効くポイント:転送量削減+キャッシュ安定化
.dockerignore が効くポイントは大きく2つです。1つ目は転送量の削減で、ビルド前の「コンテキスト収集・転送・展開」が軽くなります。特にリモートbuilderやCIでは、ここがボトルネックになりやすいので効果が出やすいです。
2つ目がキャッシュ安定化です。毎回変わるファイル(ログ、一時ファイル、ビルド成果物など)がコンテキストに混ざっていると、COPY . . で取り込む範囲が実質的に毎回変わり、コピー命令のキャッシュが崩れます。.dockerignoreで“毎回変わるもの”を排除しておくと、COPYの入力が安定し、以降のレイヤキャッシュも安定します。
3-3. .git や node_modules を除外すべき理由
.git/ や node_modules/ は、ほぼ例外なく除外した方が良い代表例です。node_modules はサイズが大きく、OSや環境によって中身が変わりやすいので、コンテキストの転送もキャッシュも壊しやすくなります。コンテナ内で npm ci して作るのが基本で、ホストの node_modules を持ち込む必要は通常ありません。
.git はさらに厄介で、ブランチ切り替えやコミットが増えるだけで中身が変わります。これがコンテキストに入ると「なぜか毎回COPYが変わる」状態になります。加えて、リポジトリの履歴やリモートURLなどが含まれるため、意図せず機密情報の持ち込みにつながることもあります。速度・安定性・機密の3点から、基本は除外で考えるのが安全です。
4. キャッシュが崩れる典型パターン(コンテキスト起因)
4-1. 生成物(dist/, build/)が混ざって毎回変わる
フロントエンドやアプリのビルド成果物(dist/, build/, .next/ など)がリポジトリ内に生成される構成だと、コンテキストが“毎回変わる”状態になりがちです。ローカルで一度ビルドしただけで大量のファイルが増え、その後の docker build でコンテキストが膨らみます。
さらに、成果物はビルド時刻や環境によって微妙に変わることがあり、COPY . . の入力を安定させにくくします。原則として、成果物ディレクトリは .dockerignore で除外し、必要ならコンテナ内ビルド(マルチステージ)で生成する方針が安定します。
4-2. ログや一時ファイル(*.log, .cache/)が混ざる
ログやキャッシュディレクトリもキャッシュ破壊の原因になります。*.log は実行するたびに増えますし、.cache/ はツールやテストが勝手に中身を書き換えます。開発中は気づきにくいですが、Dockerにとっては「入力が変わった」と判定される要因です。
これらがコンテキストに含まれた状態で COPY . . をすると、ソースコードを変えていなくてもCOPYの入力が変化し、以降のレイヤが作り直しになります。ビルドを安定させたいなら、「毎回変わるものはコンテキストから出す」という意識が必要です。
4-3. .git/ が混ざってコミットやブランチ操作で予期せず変わる
.git/ は、コミットやブランチ操作のたびに内部のファイルが変わります。そのため、コンテキストに混ざっていると「コードは変えてないのにビルドキャッシュが崩れる」現象を引き起こしやすいです。特にCIでは、チェックアウト方法やフェッチ深さの違いで .git の状態が微妙に変わり、毎回入力が違う扱いになることもあります。
また、.git をコンテナに入れると、意図せずGitメタデータがイメージに入ることがあります。たとえば内部のリモートURLや履歴など、チーム内では気にしない情報が外部配布物に混ざるのは避けたいところです。基本は .dockerignore で除外し、バージョン情報が必要ならビルド引数でコミットSHAだけ渡す、など別の設計に寄せるのが安全です。
5. 実務で使える .dockerignore の型(例つき)
5-1. 共通テンプレ:OS/エディタ/ログ/キャッシュ/成果物
まずは、どの言語でも使える共通テンプレです。プロジェクトごとに微調整して使うと便利です。
# OS / Editor
.DS_Store
Thumbs.db
*.swp
*.swo
.idea/
.vscode/
# Logs
*.log
# Caches
.cache/
.tmp/
tmp/
# Build artifacts
dist/
build/
out/
# Git
.git/
.gitignore
この例では、「毎回変わりやすいもの」「サイズが大きくなりがちなもの」「機密になり得るもの」をまとめて除外しています。まずはこのくらいを入れておき、ビルドに必要なものが除外されていないかを確認しながら調整するのが現実的です。
5-2. Node / Next.js
node_modules/
.next/
dist/
coverage/
npm-debug.log*
yarn-error.log*
Node系は node_modules とフレームワーク成果物(Nextなら.next)を除外するだけで効果が大きいです。特に node_modules はサイズも変化頻度も高く、コンテキストを爆発させる代表例です。
テストのカバレッジやデバッグログも毎回変わるので、基本は除外しておきます。Dockerfile側では、依存はコンテナ内で npm ci / pnpm install する構成に寄せると、キャッシュも安定します。
5-3. Python
__pycache__/
*.pyc
.venv/
.pytest_cache/
.mypy_cache/
Python系は __pycache__ や仮想環境(.venv)が混ざるのが典型です。これらが入っていると、環境によって内容が変わりやすく、COPYの入力を不安定にします。
ローカルの仮想環境をコンテナへ持ち込む設計は基本的に避け、コンテナ内で pip install を行う構成にします。その前提で .venv を除外すると、コンテキストをきれいに保てます。
5-4. 例外の付け方(必要なファイルだけ戻す)
.dockerignore は「除外」だけでなく、例外(戻し)も書けます。たとえば、まず大きく除外しておき、必要なファイルだけ戻すときに使います。
# まず広く除外
dist/
# ただし、必要な設定だけ戻す(例)
!dist/config.json
この例では、基本は dist/ を除外しつつ、どうしても必要な dist/config.json だけをコンテキストに含めます。例外を増やしすぎると管理が難しくなるので、「どうしても必要なときだけ、小さく戻す」を意識すると運用しやすいです。
6. 見える化と確認手順(“効いてるつもり”防止)
6-1. ビルドログでコンテキスト転送量を見る(--progress=plain)
.dockerignore が効いているか確認するには、まずビルドログで「コンテキスト送信量」を見ます。BuildKitの場合、--progress=plain を付けると、ステップごとの情報が見やすくなります。
docker build --progress=plain -t myapp .
このコマンドでは、ビルドの進行状況がテキストで出力され、コンテキストのサイズ感や、どのステップが再実行されたかを追いやすくなります。サイズが明らかに大きい場合は、.dockerignore の見直し候補です。
6-2. どの COPY でキャッシュが崩れたか特定する
次に、キャッシュが崩れている場合は「最初にキャッシュが外れた命令」を探します。Dockerfileの上から順に見ていき、最初に再実行されているのが COPY なら、コンテキスト起因の可能性が高いです。
たとえば COPY . . が毎回キャッシュミスになっているなら、コンテキストに「毎回変わるファイル」が混ざっているか、コピー範囲が広すぎることが疑えます。ここで .dockerignore を強化し、COPYの入力を安定化させると、以降のRUN(依存インストールなど)もまとめて速くなることが多いです。
6-3. 改善サイクル:コンテキスト削減 → Dockerfile順序最適化 → BuildKitキャッシュ
改善の順番は次の流れが最短で効きます。
- コンテキスト削減(
.dockerignore) - Dockerfile順序最適化(依存→ソース、COPY範囲の分割)
- BuildKitキャッシュ強化(
--mount=type=cache、remote cache)
最初からremote cacheに飛びつくより、まず“入力を小さく安定させる”ほうが効果が出やすいです。入力が毎回変わっている状態でキャッシュを共有しても、そもそもヒットしないからです。
7. まとめ
7-1. ビルドコンテキストは「ビルドの入力」
ビルドコンテキストは、docker build がビルダーに渡す入力ファイル一式です。ここが大きいほど転送・展開・差分計算が重くなり、ビルドが遅くなります。
さらに、コンテキストが不安定(毎回変わるファイルが混ざる)だと、COPY/ADD のキャッシュが崩れ、依存インストールなども巻き添えで再実行されがちです。「入力を安定させる」のが重要です。
7-2. .dockerignore は速度だけでなく、キャッシュ安定化と機密混入防止にも効く
.dockerignore は、コンテキストから不要ファイルを除外する仕組みで、転送量削減とキャッシュ安定化に効きます。加えて、.git や秘密情報を含むファイルをコンテナに持ち込まない、という意味でセキュリティ面でも重要です。
「コンテキストは小さく・変わりにくく」を意識して、.dockerignore をテンプレ化しておくと、チーム開発でも事故が減ります。
7-3. 最短の改善順:.dockerignore → Dockerfile順序 → CIキャッシュ(BuildKit)
最短の改善順は、まず .dockerignore を整え、次にDockerfileの順序やCOPY範囲を最適化し、それでも足りなければBuildKitのcache mountやremote cacheでCIを高速化、という流れです。
「ビルドが遅い」と感じたら、まずはコンテキストのサイズと安定性を疑う。ここが分かるだけで、原因に最短で辿りつけるようになります。
8. 参考リンク
- Dockerfile reference(COPY/ADDと挙動)
https://docs.docker.com/reference/dockerfile/ - Docker build(ビルドコンテキストの説明)
https://docs.docker.com/build/building/context/ - .dockerignore の仕様とパターン
https://docs.docker.com/build/building/context/#dockerignore-files - BuildKit / buildx(CIキャッシュ共有)
https://docs.docker.com/build/buildkit/
https://docs.docker.com/build/buildx/