2026年5月26日
2026年5月26日
DockerでNginxを動かす方法|公式イメージの起動・設定ファイルの差し替え・Composeでの運用
DockerでNginxを動かすだけなら、公式イメージを使って docker run -d -p 8080:80 nginx の1行で始められます。実際に使うときに迷うのは、その先の「自分のHTMLや設定ファイルをどう渡すか」「設定を変えたらどう反映するか」「ポートを外にどう公開するか」です。この記事では、公式イメージの中身の構造を押さえたうえで、静的ファイルの配信、設定ファイルの差し替えと検証、Composeでの管理、よくあるエラーの見分け方までを順に説明します。
前提はLinux上のDocker EngineとDocker Compose v2(docker compose コマンド)です(2026年10月現在)。Docker Desktopでも手順はほぼ同じです。
まずは公式イメージで起動して中身を確かめる
docker run -d --name web -p 8080:80 nginx:stable
curl -I http://localhost:8080/
-p 8080:80 は「ホストの8080番をコンテナの80番につなぐ」という意味です。ブラウザで http://localhost:8080/ を開いて「Welcome to nginx!」のページが出れば動いています。
タグは latest のままにせず、stable や 1.xx のように系列を指定しておくのがおすすめです。latest はメインライン版を指すため、知らないうちに大きく更新されることがあります。具体的なバージョン番号はDocker Hubの公式nginxページで確認してください。
設定を差し替える前に、公式イメージがどこに何を置いているかを見ておくと後の作業が楽になります。
docker exec web ls /etc/nginx/conf.d /usr/share/nginx/html
docker exec web cat /etc/nginx/conf.d/default.conf
覚えておきたい場所は次の3つです。
/etc/nginx/nginx.conf:全体の設定。conf.d/*.confを読み込む行が入っています。/etc/nginx/conf.d/default.conf:80番で待ち受けるサーバー設定。通常はここを差し替えます。/usr/share/nginx/html:既定のドキュメントルート。
また、公式イメージではアクセスログとエラーログが標準出力・標準エラーにつながっているので、ログは docker logs web で見られます。コンテナ内のログファイルを探す必要はありません。
自分のHTMLを配信する(バインドマウント)
ホストのディレクトリをドキュメントルートに重ねると、イメージを作り直さずにファイルを配信できます。
mkdir -p html && echo '<h1>Hello from Docker</h1>' > html/index.html
docker rm -f web
docker run -d --name web -p 8080:80 \
-v "$(pwd)/html:/usr/share/nginx/html:ro" \
nginx:stable
末尾の :ro は読み取り専用の指定です。Nginxは配信するだけなので書き込み権限は要りませんし、万一コンテナが侵害されてもホスト側のファイルを書き換えられにくくなります。
ここでつまずきやすいのが403エラーです。ファイルの権限がコンテナ内のNginxの実行ユーザーから読めない、ディレクトリに実行権限(x)がない、といった原因が多く、docker logs web に Permission denied が出ていればこれに当たります。SELinuxが有効な環境(RHEL系など)では、マウント指定に :z や :Z を付けてラベルを付け直さないと読めないことがあります。
本番で配る場合は、バインドマウントより COPY でファイルをイメージに焼き込むほうが、どのサーバーでも同じ中身になるので扱いやすくなります。
FROM nginx:stable
COPY html/ /usr/share/nginx/html/
COPY default.conf /etc/nginx/conf.d/default.conf
設定ファイルを差し替えて検証する
default.conf を手元に用意して、コンテナの同じ場所にマウントします。次はキャッシュ用のヘッダーと、存在しないパスで404を返す最小構成の例です。
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(css|js|png|jpg|svg|webp)$ {
expires 7d;
add_header Cache-Control "public";
}
}
docker run -d --name web -p 8080:80 \
-v "$(pwd)/html:/usr/share/nginx/html:ro" \
-v "$(pwd)/default.conf:/etc/nginx/conf.d/default.conf:ro" \
nginx:stable
設定を編集したら、反映の前に文法チェックをするのが安全です。
docker exec web nginx -t # 文法チェック
docker exec web nginx -s reload # 問題なければ再読み込み
nginx -t で syntax is ok と test is successful の両方が出てから reload します。チェックを飛ばして docker restart すると、設定に誤りがあった場合にコンテナが起動せず、サイトが止まります。
注意点として、ファイル1つだけをマウントしている場合、エディタによっては保存時にファイルを置き換える(別のinodeになる)ため、コンテナ側から変更が見えないことがあります。変更が反映されないときは、ファイルではなく conf.d ディレクトリごとマウントする方法に切り替えると解消しやすくなります。
公式イメージには、/etc/nginx/templates/ に置いた *.template を起動時に環境変数で置き換えて conf.d に書き出す仕組みもあります。ドメイン名やバックエンドのアドレスを環境ごとに変えたいときに便利です。置き換えの対象は定義済みの環境変数に限られる仕様ですが、Nginx自身の変数($uri など)と同じ名前の環境変数があると意図せず置き換わるため、使う前に公式イメージの説明で仕様を確認してください。
Composeで管理する
オプションが増えてきたら、compose.yaml にまとめておくと再現しやすくなります。
services:
web:
image: nginx:stable
ports:
- "127.0.0.1:8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./conf.d:/etc/nginx/conf.d:ro
restart: unless-stopped
docker compose up -d
docker compose exec web nginx -t
docker compose exec web nginx -s reload
docker compose logs -f web
ports を "127.0.0.1:8080:80" と書くと、ホスト自身からしか接続できなくなります。開発用や、前段に別のリバースプロキシを置く場合はこの書き方が安全です。"8080:80" とだけ書くと全インターフェースで公開されます。しかもDockerは公開ポートのためにiptablesのルールを直接追加するので、ufwなどのファイアウォールで閉じたつもりでも外から届くことがあります。公開範囲は ports の書き方で決める、と考えておくと事故を防げます。
restart: unless-stopped は、ホストの再起動やプロセスの異常終了のあとに自動で起動し直す設定です。手で docker compose stop したときは起動しません。
他のコンテナに転送する場合
同じComposeファイル内のサービスには、サービス名でアクセスできます。たとえば app という名前のアプリが3000番で待ち受けているなら、proxy_pass http://app:3000; と書けます。
このとき、Nginxは起動時に転送先の名前を解決しようとするため、app がまだ存在しないと host not found in upstream で起動に失敗します。depends_on で起動順を指定する、転送先を先に起動する、といった対処が必要です。転送の詳細な設計は本記事の範囲を超えるので、ここでは「サービス名で届く」「起動順に注意」の2点だけ押さえておいてください。
よくあるエラーと確認の順番
うまく動かないときは、次の順で確かめると原因を絞り込みやすくなります。
- コンテナが動いているか:
docker ps -aでExitedになっていれば、docker logs webの最後のほうに設定エラーの行(emergで始まるもの)が出ています。ファイル名と行番号が書かれているので、そこを直します。 - ポートが使われていないか:
bind: address already in useは、ホスト側の同じポートを別のプロセス(ホストに直接入れたNginxやApacheなど)が使っている状態です。sudo ss -ltnp | grep :8080で使っているプロセスを確認し、止めるか別のポートを使います。 - 403が出る:前述のファイル権限やSELinuxのラベル、
indexに指定したファイルが存在しないことが主な原因です。 - 502が出る:転送先のコンテナが止まっている、ポート番号が違う、転送先のアプリが
127.0.0.1でしか待ち受けていない、のいずれかが多いです。転送先コンテナのdocker logsも合わせて確認します。 - 変更が反映されない:ブラウザのキャッシュ、マウントのinode問題、
reloadのし忘れを順に疑います。
運用で気をつけたいこと
- イメージの更新:Nginxにも脆弱性の修正版が出ます。定期的に
docker compose pullしてからdocker compose up -dで作り直す運用にしておきます。作り直しても、マウントしている設定ファイルやHTMLは消えません。 - root権限で動かしたくない場合:通常の公式イメージはマスタープロセスがrootで起動します。気になる場合は、非rootで動くように作られた
nginxinc/nginx-unprivilegedというイメージがあります。こちらは既定の待ち受けが8080番になるなど設定の前提が違うので、説明をよく読んでから切り替えてください。 - HTTPS:証明書と秘密鍵をホストからマウントして
listen 443 ssl;を設定します。秘密鍵はイメージにCOPYせず、マウントで渡すほうが漏えいのリスクを抑えられます。
まとめ
- 公式イメージは
/etc/nginx/conf.d/default.confと/usr/share/nginx/htmlを差し替えるのが基本です。ログはdocker logsで見られます。 - HTMLや設定はバインドマウント(
:ro付き)で渡し、本番ではCOPYでイメージに含める方法も検討します。 - 設定を変えたら
nginx -tで確認してからnginx -s reloadします。 portsは127.0.0.1:を付けると外部に公開されません。Dockerの公開ポートはファイアウォールの設定を素通りすることがあります。- タグは系列を固定し、定期的に pull して作り直します。