Docker Composeのextendsで共通設定を複数ファイルに分割管理する方法|環境別オーバーライドとの使い分け

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Docker > Docker Composeのextendsで共通設定を複数ファイルに分割管理する方法|環境別オーバーライドとの使い分け
「複数のcompose.ymlに同じservice定義をコピペしていて、片方だけ直し忘れて事故った」
「開発用と本番用でビルド設定やログ設定だけ違うのに、サービス定義を丸ごと2重管理している」
「サービスが増えるたびにcompose.ymlが肥大化して、チームで同じファイルを触るとコンフリクトが頻発する」
そんな悩みを抱えたままcompose.ymlを運用しているチームは少なくありません。

Docker Composeには、共通のサービス定義を別ファイルに切り出して複数サービス・複数プロジェクトから再利用できるextends、Compose v2.20以降で使えるファイルを丸ごと取り込むinclude、Nginx等の設定ファイルをコンテナに注入できるconfigsという3つの分割・管理機能があります。
公式ドキュメントでの扱いが小さく、「override(環境別の設定上書き)」との違いを理解しないまま使うと、意図しない設定崩れを招くことがあります。

この記事では、extendsの基本構文から共通base定義を切り出す実践例、includeキーによるファイル分割設計(アプリ・インフラ・監視の3ファイル構成パターンを含む)、configsキーによる設定ファイルのコンテナへの注入、override(docker-compose.override.yml)との使い分け、設定が反映されない時のトラブルシュートまで、実サーバーの確認例を交えて解説します。

動作確認環境: Rocky Linux 9.4 / Ubuntu 24.04 LTS(Docker Engine 26.x・Docker Compose Plugin v2.27)

この記事のポイント

・extendsは共通のサービス定義を別ファイルに切り出し、複数サービス・複数プロジェクトから再利用する仕組み
・includeはCompose v2.20以降で使えるファイル丸ごと取り込み機能。アプリ・インフラ・監視の3ファイル構成でチームの担当領域を分離できる
・configsキーは設定ファイルをコンテナに注入する仕組み。bind mountのホストパス依存を解消できる
・override(docker-compose.override.yml)は同一プロジェクト内の環境差分、extendsは共通化そのものが目的で役割が違う
・ports・volumesなどのリスト項目はマージ時に結合ではなく丸ごと置き換えになる点が事故の原因になりやすい
・docker compose configで結合後の設定を出力し、意図した内容にマージされているかを毎回確認するのが鉄則


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

extendsとは何か|Compose Specにおける共通設定の継承の仕組み

複数のマイクロサービスを1つのcompose.ymlで管理していると、ロギング設定やヘルスチェック、リスタートポリシーなど「どのサービスにも共通して書きたい項目」が増えていきます。
これをサービスの数だけコピペしていると、設定変更のたびに全サービスを1つずつ直す必要が出てきて、修正漏れの温床になります。

1. なぜcompose.ymlが肥大化するのか

現場でよくあるのは、開発チームが増えるにつれてサービス数が10・20と増え、logging:やhealthcheck:、restart:といった定型ブロックがサービスごとに重複していくパターンです。
フロントエンド・APIサーバー・バックグラウンドワーカー・DB・キャッシュ・メッセージキューと組み合わせると、compose.ymlが400行を超えることも珍しくありません。ネットワーク定義・ボリューム定義・環境変数が混在し、どのサービスがどのネットワークに属しているのか把握するだけで時間が取られます。チーム開発では同じファイルを複数人が触るためgitのコンフリクトも頻発します。

compose.ymlの分割・共通化には、目的に応じて4つのアプローチがあります。
・extends:サービス定義の共通部分を別ファイルに集約し、複数サービスが継承する(DRY化)
・include(v2.20以降):サービス群を丸ごと別ファイルに分離し、ルートのcompose.ymlから取り込む(ファイル分割)
・configs:設定ファイルをコンテナに注入する。bind mountのホストパス依存を解消し、どのサービスが何を使っているかをcompose.ymlで明示できる
・override(-f複数指定):同一プロジェクト内で開発・本番の環境差分を重ねる(環境分離)

2. extends構文の基本形

extendsは、file:で参照先ファイル、service:で参照先サービス名を指定し、そのサービス定義をベースに継承します。

# extendsの最小構文 services: web: extends: file: common-services.yml service: app-base ports: - "8080:80"

webサービスはcommon-services.yml内のapp-baseサービスの定義(イメージ・環境変数・ヘルスチェック等)を継承したうえで、ports:だけを自分で上書き追加します。

extendsで共通base設定を複数サービスに継承する実践例

3. common-services.ymlに共通設定を切り出す

まず、複数サービスで共有したい設定だけを独立したファイルにまとめます。

# common-services.yml(共通設定の置き場) services: app-base: image: myapp:latest environment: - TZ=Asia/Tokyo - LOG_LEVEL=info logging: driver: json-file options: max-size: "10m" max-file: "3" healthcheck: test: ["CMD", "curl", "-f", "http://localhost/healthz"] interval: 30s timeout: 5s retries: 3 restart: unless-stopped

続いて、各サービス側のcompose.ymlからapp-baseを継承します。

# compose.yml(本体:web/workerがapp-baseを継承する) services: web: extends: file: common-services.yml service: app-base ports: - "8080:80" worker: extends: file: common-services.yml service: app-base command: ["python", "worker.py"]

webとworkerは、ロギング設定(json-file・10MB×3世代)やヘルスチェック、リスタートポリシーを個別に書かずに共有できます。
共通部分を1箇所に集約したことで、ログ設定を変更したい場合はcommon-services.ymlを1回直すだけで両方に反映されます。

4. docker compose configで継承結果を確認する

extendsは「マージ後の見えない設定」が増える仕組みなので、実際にどうマージされたかを都度確認する習慣が重要です。

# 結合後の実効設定をYAMLで出力して確認する $ docker compose config # 出力例(環境により異なります) services: web: environment: LOG_LEVEL: info TZ: Asia/Tokyo healthcheck: interval: 30s retries: 3 test: - CMD - curl - -f - http://localhost/healthz timeout: 5s image: myapp:latest logging: driver: json-file options: max-file: "3" max-size: 10m ports: - mode: ingress target: 80 published: "8080" restart: unless-stopped

docker compose configは本番投入前の確認作業として必ず実行すべきコマンドです。継承ミスや意図しない上書きは、ほとんどがこの1コマンドで事前に発見できます。
Docker Composeの設計・共通化をさらに深く体系的に学びたい方は、Dockerマスター講座で実践的なコンテナ運用ノウハウを扱っています。

extendsとoverride(compose.override.yml)の違いと使い分け

5. overrideは「環境差分」、extendsは「共通化」が目的

似たような仕組みに見えて、extendsとoverrideは目的がまったく違います。
・override(docker-compose.override.ymlや-f複数指定):同一プロジェクトの中で「開発環境だけvolumesをbind mountにしたい」「本番だけレプリカ数を増やしたい」といった環境差分を後から重ねる仕組み
・extends:プロジェクトやファイルをまたいで「共通のベース定義」そのものを1箇所に集約し、複数サービスから参照する仕組み
・include(v2.20以降):Composeファイルを丸ごと分割し、複数のYAMLをひとつのプロジェクトとして扱う仕組み

overrideは「同じプロジェクトの縦の差分」、extendsは「複数サービス・複数プロジェクトの横の共通化」、includeは「ファイル単位の物理的な分割」と整理すると混同しにくくなります。

6. 両方を組み合わせた設計パターン

実務では両者を併用するのが一般的です。共通のapp-base定義はextendsで集約し、環境ごとの差分はoverrideで重ねます。

# compose.override.yml(本番のみポート・レプリカ数を変更する差分) services: web: ports: - "443:80" deploy: replicas: 3

# 本番環境ではoverrideファイルを明示的に指定して起動する $ docker compose -f compose.yml -f compose.override.yml up -d

extendsで「何を共有するか」を固定し、overrideで「どこを環境ごとに変えるか」を分離すると、compose.ymlの見通しが大きく改善します。

includeキーでCompose設定ファイルをチーム・機能単位に分割する方法(v2.20以降)

Compose v2.20以降で使えるincludeキーは、Composeファイルを丸ごとインクルードする機能です。extendsが「サービス設定の継承(DRY化)」なのに対し、includeは「ファイル全体の取り込み(物理分割)」が目的です。チームや機能単位でファイルを分けて並行開発したいときに有効です。

7. includeキーの基本構文とバージョン確認

includeを使う前に、Docker Compose v2.20以降が動作していることを確認します。

# バージョン確認 $ docker compose version # 出力例(環境により異なります) Docker Compose version v2.27.0

v2.20未満の場合はアップグレードしてください。

# Ubuntu / Debian系のアップグレード $ sudo apt update && sudo apt upgrade docker-compose-plugin # Rocky Linux / RHEL 9系のアップグレード $ sudo dnf update docker-compose-plugin

基本のディレクトリ構成例です。

myproject/ ├── compose.yml # ルートCompose(includeのみ書く) ├── services/ │ ├── api/ │ │ ├── compose.yml # APIサービス定義 │ │ └── Dockerfile │ ├── db/ │ │ └── compose.yml # DBサービス定義 │ └── cache/ │ └── compose.yml # キャッシュサービス定義 └── .env

8. ルートのcompose.ymlにincludeを記述する

# compose.yml(ルート) include: - path: ./services/api/compose.yml - path: ./services/db/compose.yml - path: ./services/cache/compose.yml networks: app-network: driver: bridge

docker compose up -dを実行すると、インクルードした全ファイルのサービスがまとめて起動します。

$ docker compose up -d # 出力例(環境により異なります) [+] Running 3/3 ✔ Container myproject-db-1 Started ✔ Container myproject-cache-1 Started ✔ Container myproject-api-1 Started # インクルードされたサービスの一覧を確認する $ docker compose config --services api cache db

9. アプリ・インフラ・監視の3ファイル構成(推奨パターン)

サービス数が増えてきたプロジェクトでは、アプリ層・インフラ層・監視層の3ファイルに分割する構成が特に管理しやすくなります。インフラ担当者はinfra/compose.infra.ymlだけ、アプリ担当者はapp/compose.app.ymlだけを触ればよくなり、担当外の設定を誤って変更するリスクが下がります。

project/ ├── compose.yml # メイン(includeを束ねる) ├── app/ │ ├── compose.app.yml # アプリ層(web, worker) │ └── Dockerfile ├── infra/ │ ├── compose.infra.yml # インフラ層(db, redis) │ └── initdb/ │ └── 01_init.sql └── monitoring/ ├── compose.monitoring.yml # 監視層(prometheus, grafana) └── prometheus.yml

# compose.yml(メインファイル) include: - app/compose.app.yml - infra/compose.infra.yml - monitoring/compose.monitoring.yml

各ファイルの実装例です。

# app/compose.app.yml(アプリ層) services: web: build: . ports: - "8080:8080" environment: DATABASE_URL: postgresql://postgres:secret@db:5432/myapp depends_on: db: condition: service_healthy worker: build: . command: celery -A myapp worker -l info depends_on: - redis - db

# infra/compose.infra.yml(インフラ層) services: db: image: postgres:16 volumes: - pgdata:/var/lib/postgresql/data - ./initdb:/docker-entrypoint-initdb.d environment: POSTGRES_PASSWORD: secret POSTGRES_DB: myapp healthcheck: test: ["CMD", "pg_isready", "-U", "postgres"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine volumes: - redisdata:/data volumes: pgdata: redisdata:

infra/compose.infra.yml内の./initdb:/docker-entrypoint-initdb.dは、このファイルが置かれているinfra/ディレクトリを基準としてパスが解決されます(後述の注意点を参照)。git diffもファイル単位で見通しよくなるため、コードレビューの負荷が下がります。

10. 環境別overrideとincludeを組み合わせる

includeで共通部分を取り込んだうえで、環境ごとの差分は-fオプションで重ねます。includeとoverride(-f)は直交した仕組みなので、組み合わせても問題ありません。

# 開発環境(ソースコードをバインドマウントし、デバッグモードを有効化) $ docker compose -f compose.yml -f compose.dev.yml up # 本番環境(ビルドイメージをそのまま使用) $ docker compose -f compose.yml -f compose.prod.yml up

# compose.dev.yml(開発用の追加設定) services: web: volumes: - .:/app environment: DEBUG: "true"

includeで取り込まれたサービス(dbやredisなど)も-fで上書きできます。「includeで物理分割、-fで環境差分」と役割を分けると、compose.yml全体の構造が把握しやすくなります。

11. includeを使う際の注意点

・パスの解決基準:インクルード先のファイル内で使うパス(build: .やボリュームのパスなど)は、そのファイルが置かれているディレクトリを基準として解決されます。infra/compose.infra.yml内に./initdb:/docker-entrypoint-initdb.dと書いた場合、参照されるのはinfra/initdb/です。ルートのcompose.ymlのディレクトリではない点がoverride(-f)との大きな違いです
・ネットワーク・ボリュームの定義場所:インクルード先で共有ネットワーク(app-network等)を参照する場合、そのネットワークはルートのcompose.ymlで一元定義するのが無難です。複数のインクルード先ファイルに同じネットワーク名を定義するとエラーになります
・.envファイルのパス:インクルード先のファイルはそのファイルが置かれているディレクトリの.envを参照します。ルートの.envを全インクルード先で共有したい場合は、env_fileキーで明示的に指定します(v2.24以降)
・バージョン確認:include機能はDocker Compose v2.20以降が必要です。古い場合はsudo apt upgrade docker-compose-pluginまたはsudo dnf update docker-compose-pluginでアップグレードします
・サービス名の重複:複数のインクルード先ファイルに同じサービス名が存在するとエラーになります。チーム間で命名規則を決めておくことで防止できます

configsキーで設定ファイルをコンテナに注入する方法

NginxのリバースプロキシやWebアプリの設定ファイルをコンテナに渡す方法として、bind mount(volumeのホストパス指定)がよく使われますが、これには課題があります。開発機では./config/nginx.confでも本番サーバーでは/opt/app/config/nginx.confになることがあり、環境ごとにcompose.ymlを書き換える必要が生じます。また、volumesセクションに並んだパスのリストから、サービスと設定ファイルの対応関係を追うのが難しくなるという問題もあります。

これを解決するのが、Docker Composeのトップレベルキー configs です。

12. configsセクションの基本構文

トップレベルのconfigs:セクションでファイルを登録し、サービスのconfigs:リストで参照します。

# compose.yml configs: nginx_config: # configの名前(任意) file: ./nginx.conf # ホスト側のファイルパス services: nginx: image: nginx:latest ports: - "80:80" configs: - source: nginx_config # configの名前 target: /etc/nginx/nginx.conf # コンテナ内の配置先 mode: 0444 # ファイルの権限(8進数)

・source:トップレベルのconfigsセクションで定義した名前
・target:コンテナ内のフルパス。省略時は /<config名>
・mode:8進数のパーミッション。デフォルトは 0444(全員読み取り可)

13. 複数サービスで同じconfigを共有する

NginxとFluentdのように複数サービスが同じ設定ファイルを参照するケースでは、同一のconfigを複数サービスから参照できます。

configs: shared_config: file: ./shared-settings.conf services: app: image: myapp:latest configs: - source: shared_config target: /app/config/settings.conf monitor: image: fluentd:latest configs: - source: shared_config target: /etc/fluent/settings.conf

同じconfigを複数のサービスが参照していても、ファイルの実体はDockerが一元管理します。bind mountと比べてホストパスへの依存がconfigsセクションに集約されるため、どのサービスが何の設定ファイルを使っているかが一目でわかります。

14. secretsとconfigsの使い分け

同様にファイルをコンテナに注入する機能としてsecretsがあります。使い分けの判断基準は「そのファイルを見られたときのリスク」です。
比較項目 configs secrets
主な用途 nginx.conf・アプリ設定ファイル パスワード・APIキー・TLS秘密鍵
暗号化 なし あり(Swarm)
デフォルトのパス /<config名> /run/secrets/<secret名>
デフォルトのmode 0444(全員読み取り可) 0400(所有者のみ読み取り可)
Swarm対応 ○ ○
実際の運用では「Nginxの設定ファイル → configs、DBのパスワード → secrets」のように分けて管理します。configsとsecretsを組み合わせた例は次のとおりです。

configs: nginx_config: file: ./nginx.conf secrets: db_password: file: ./db_password.txt # .gitignoreで除外 services: nginx: image: nginx:latest configs: - source: nginx_config target: /etc/nginx/conf.d/default.conf app: image: myapp:latest secrets: - db_password db: image: postgres:16 secrets: - db_password environment: POSTGRES_PASSWORD_FILE: /run/secrets/db_password

注意:secretsのファイル(db_password.txt等)は必ず.gitignoreでリポジトリから除外してください。誤ってコミットすると認証情報が漏洩するリスクがあります。

トラブルシュート|extendsやconfigsで設定が反映されない時の調査手順

15. 手順1: リストのマージではなく置き換えになっていないか疑う

extendsやoverrideでは、Compose全体のマージ規則が適用されます。
environment:のようなマッピング(連想配列)はキー単位でマージされますが、ports:やvolumes:のようなリストは結合ではなく丸ごと置き換えになります。
これに気づかず「継承元のvolumesも残るはず」と思い込んでいると、共通base側で定義したマウントがごっそり消えるという事故が起きます。

# app-base側のvolumes(共有ライブラリのマウント) services: app-base: volumes: - shared_lib:/opt/shared # web側でvolumesを追加したつもりが、実際は置き換わってしまう例 services: web: extends: file: common-services.yml service: app-base volumes: - ./web-static:/var/www/static # ← ここに shared_lib のマウントを書き足さないと消える

注意:共通base側のvolumesやportsを引き継ぎたい場合は、継承側でも同じ項目を明示的に書き足す必要があります。「継承すれば自動的に足し算になる」という思い込みが最も多い事故原因です。

16. 手順2: docker compose config --servicesとpsで実際の反映状況を確認する

# 認識されているサービス一覧を確認(extends先のservice名の誤字はここで発覚しやすい) $ docker compose config --services web worker db # 起動中コンテナの実際のマウント・ポート状況を確認 $ docker compose ps --format "table {{.Name}} {{.Ports}}" NAME PORTS myapp-web-1 0.0.0.0:8080->80/tcp myapp-worker-1

17. 手順3: 参照ファイルのパスミスを疑う

extends.file:のパスは、それを記述しているcompose.ymlからの相対パスで解決されます。
サブディレクトリに移動した際にパス指定を直し忘れると、以下のようなエラーで起動そのものが失敗します。

$ docker compose up -d service "web" refers to undefined service "app-base": could not find common-services.yml # 対処: extends.fileのパスを実際のディレクトリ構成に合わせて修正する extends: file: ../shared/common-services.yml service: app-base

18. 手順4: configsで設定ファイルを変更しても反映されない

docker compose up -dは既存のconfigを再作成しません。ホスト側のファイルを変更してconfigを更新するには、サービスを一度停止してから再起動する必要があります。

# サービスを停止・削除してから再起動(configも再作成される) $ docker compose down $ docker compose up -d # 再起動後にコンテナ内の設定ファイルを確認する $ docker compose exec nginx cat /etc/nginx/nginx.conf

19. 手順5: includeでファイルが見つからないエラーへの対処

include:に指定したパスが間違っているか、ファイルが存在しない場合に次のエラーが発生します。

# エラー例(出力例、環境により異なります) Error response from daemon: open /path/to/infra/compose.infra.yml: no such file or directory

includeに指定するパスはルートのcompose.ymlが置かれているディレクトリからの相対パスです。infra/compose.infra.ymlのようにサブディレクトリを含めて書きます。docker compose configを実行してエラーが出る場合は、まずパスの確認から始めるのが早道です。

20. 手順6: includeでサービス名が重複するエラーへの対処

複数のincludeファイルに同名のサービスが定義されていると、次のエラーが発生します。

# エラー例(出力例、環境により異なります) Error: service "redis" already defined in included file

includeで取り込まれたファイル間でサービス名の衝突は許されません。共有したいサービスはどちらか一方のファイルにのみ定義し、他のファイルからはdepends_onで参照してください。チーム間でサービス命名規則を決めておくことで未然に防止できます。

21. よくある原因チェックリスト

・リスト項目の消失:ports・volumesなど継承元にあった設定が、継承側の同項目で丸ごと置き換わっていないか
・service名の誤字:extends.serviceに指定した名前が参照先ファイル内のサービス名と一致しているか
・相対パスのズレ:extends.fileのパスが、compose.ymlの実際の配置場所から見て正しいか
・循環参照:AがBを継承し、BがAを継承するような循環構成になっていないか(Composeはこれを検出してエラーにする)
・configsの未反映:ホスト側の設定ファイルを変更した後にdocker compose downして再起動しているか
・includeパスの誤字:ルートcompose.ymlからの相対パスが正しいか(docker compose configを実行してエラーが出た場合はパスの問題)
・includeサービス名の重複:複数のincludeファイルに同じサービス名が定義されていないか
・本番反映前の未確認:docker compose configで実効設定を確認せずに本番へupしていないか

本記事のまとめ

やりたいこと 使う仕組み・確認コマンド
共通のサービス定義を複数サービスで使い回したい 共通ファイルを作りextends.file/serviceで継承する
compose.ymlをサービス・チーム単位に物理分割したい ルートcompose.ymlにinclude: - path: ./xxx.ymlを書く(v2.20以降)
アプリ・インフラ・監視を別担当で管理したい 3ファイル構成でincludeし、担当者が自分のファイルだけ触れる運用にする
設定ファイルをコンテナに注入したい(bind mountを使わず) configs: - file: ./nginx.confをトップレベルに定義してサービスから参照する
パスワード・APIキー等の機密情報を注入したい secrets:を使う(デフォルトパス:/run/secrets/・mode 0400)
開発・本番で一部だけ設定を変えたい docker-compose.override.ymlや-f複数指定で重ねる
継承後の実際の設定を確認したい docker compose configで実効設定を出力する
継承元のvolumes・portsが消えてしまう リストは置き換えのため、継承側にも明示的に書き足す
extends先のファイルが見つからないと言われる extends.fileの相対パスを配置場所に合わせて修正する
includeファイルが見つからないエラーが出る ルートcompose.ymlからの相対パスを確認し、ファイル名を正確に指定する
インクルードされたサービス一覧を確認したい docker compose config --services
extendsは正しく使えば、共通設定の二重管理をなくし、compose.ymlの保守性を大きく高めてくれる仕組みです。
includeはサービスが増えてcompose.ymlが肥大化したとき、アプリ・インフラ・監視の3ファイルに分割してチームの担当領域を明確にできる強力な手段です。git diffもファイル単位で見通しよくなり、並行開発時のコンフリクトも減ります。
configsはNginx設定等をbind mountなしでコンテナに注入し、secretsと組み合わせることで設定ファイルと機密情報を適切に分けた運用が実現できます。
一方で「リストは結合ではなく置き換え」というマージ規則を知らずに使うと、静かに設定が欠落する事故につながります。
本番へ反映する前に必ずdocker compose configで実効設定を確認する運用を、チームのルールとして定着させておきましょう。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、本記事で紹介したDocker Composeの共通化・環境分離・設定ファイル管理の設計をさらに深く学べる講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。