開発

Djangoのデプロイ手順:gunicorn+nginx構成と本番設定・配置先の選び方

Djangoのデプロイ手順:gunicorn+nginx構成と本番設定・配置先の選び方

Djangoのデプロイでつまずく場所は、コマンドの並びではありません。runserverで動いていたものが、静的ファイルの配信経路とHTTPSの終端位置が変わった途端に崩れます。gunicornとnginxの役割分担、collectstaticとSTORAGES設定、本番設定の点検、配置先の選定までを、Django 6.1系の公式ドキュメントとgunicornの既定値から組み立てます。

まとめ:Djangoのデプロイで先に決める4つの分岐と構成の結論

デプロイ作業に入る前に決めるべきは、配置先、静的ファイルの出口、設定ファイルの切り分け方、HTTPSをどこで終端するか、の4点です。決めずに始めると、本番で502や静的ファイルの404を踏んでから戻る羽目になります。

構成の既定解は今もgunicorn+nginxです。gunicornの既定ワーカークラスであるsyncについて、公式Designは本番で「Requires a buffering proxy (nginx, HAProxy) for production」と明記しています。静的ファイルの設定は書き方が変わり、STATICFILES_STORAGEとDEFAULT_FILE_STORAGEはDjango 4.2で非推奨、5.1で削除されました。手順記事の多くはこの断絶より前に書かれており、設定名を写しても効きません。

配置先はAWSの提供状況が動いています。App Runnerは新規顧客に開かれておらず、公式ガイドが移行先としてAmazon ECS Express Modeを案内している状態。EC2かコンテナかは静的ファイル・常駐処理・DB接続の3点で分かれます。Django自体の全体像はDjangoとMVTパターンの入門解説を前提とし、ここでは本番に出す工程だけを扱います。

gunicornとnginxの役割分担とワーカー数・タイムアウトの設定値

2つのプロセスの担当を先に固定します。曖昧なまま設定を写すと、秒数が噛み合いません。

WSGIの受け口をgunicornに置きnginxへ前段を任せる分担

gunicornはWSGIアプリケーションサーバで、Djangoが生成したwsgi.pyのapplicationを読み込んで常駐します。起動はgunicorn config.wsgi:applicationの形。nginxが担当するのは、TLS終端、静的ファイルの直接配信、バッファリング、転送の4つです。この分担を崩すと壊れます。gunicornを直接インターネットへ向ければ、ヘッダを少しずつ送りつけるだけでsyncワーカーが占有され、ワーカー数ぶんの接続で全体が止まる。静的ファイルまで通せば、Pythonプロセスが配信でワーカーを消費します。

ワーカー数を(2×コア数)+1で決めるときの上限とメモリ試算

gunicornのworkersの既定値は1で、そのまま本番に出すと1リクエストの処理中に他が待たされます。公式Designが示す出発点は「workers = (2 × CPU cores) + 1」で、「4–12 workers」という範囲も併記されています。2コアなら5、4コアなら9。ただしメモリが先に天井を打ちます。ワーカー1つが150MBから300MB程度を占めるなら9ワーカーで最大2.7GBになり、2GBのインスタンスでは即スワップです。実測のプロセスサイズを掛けて空きメモリに収まる数まで下げてください。

timeout既定30秒とsystemd常駐時に噛み合わせる設定値

gunicornのtimeoutの既定値は30秒で、超えたワーカーは強制終了されます。graceful_timeoutも既定30秒、keepaliveは2秒。噛み合わないのはnginx側のproxy_read_timeout(既定60秒)との関係で、gunicornが先にワーカーを落とすとnginxは502を返します。帳票生成のように30秒を超える処理があるなら、両者の秒数を揃えるか非同期ジョブへ逃がすかの二択。常駐化はsystemdのserviceユニットで行い、Restart=alwaysとEnvironmentFileを組み合わせます。Unixドメインソケットで繋ぐなら、実行ユーザーが違うためソケットのグループとパーミッションを揃えないとPermission deniedで弾かれます。

collectstaticとSTORAGESで決まる静的ファイルの配信経路

管理画面のCSSが当たらない事故は、ほぼこの章の設定に集約されます。5.1での設定名の変更も含めて整理します。

STATIC_ROOTへの収集タイミングと5.1で削除された旧設定名の対応

DEBUG = FalseではDjangoによる静的配信が止まるため、STATIC_ROOTへ集めた1箇所をnginxやCDNから配信します。公式チェックリストの例はSTATIC_ROOT = "/var/www/example.com/static/"とSTATIC_URL = "/static/"。実行は「コードを配置した後、プロセスを入れ替える前」に固定します。逆だと新しいテンプレートが古い静的ファイルを参照する時間が生まれるためです。設定名も変わりました。STATICFILES_STORAGEとDEFAULT_FILE_STORAGEは4.2で非推奨、5.1で削除され、現行はSTORAGESという1つの辞書にdefaultとstaticfilesの2キーを持たせます。旧設定名を残したまま上げると、設定は無視されて既定のストレージが使われます。

ManifestStaticFilesStorageでハッシュを付ける効果と失敗する条件

ブラウザキャッシュを効かせつつ更新を反映させるなら、staticfilesのBACKENDへdjango.contrib.staticfiles.storage.ManifestStaticFilesStorageを指定します。collectstaticの実行時にファイル内容のハッシュを名前へ埋め込み、staticfiles.jsonという対応表を作る仕組みです。落とし穴は、参照されているファイルが1つでも欠けているとcollectstaticがエラーで止まる点。使わなくなったJSを消して参照が残っていると、そこでリリースが止まります。

WhiteNoiseでアプリ内配信に寄せる条件とnginx直配信との線引き

静的ファイルをアプリ側から配信する選択肢がWhiteNoiseです。最新は6.12.0(2026年2月27日リリース・Python 3.10以上)で、MIDDLEWAREのSecurityMiddleware直下に1行足すだけで動きます。コンテナで配布し前段にALBやCloudFrontを置くならWhiteNoiseに寄せ、構成部品を1つ減らす。コンテナ内にnginxを同居させると、ログの取り回しもヘルスチェックも複雑になります。逆にEC2へ直接置いてnginxがTLS終端をしているなら、静的ファイルはnginxのlocationブロックで返すほうが速い。ユーザーのアップロードファイルはMEDIA_ROOTで別系統に分け、複数台構成にする時点でS3などへ移します。1台のうちはローカルパスでも動いてしまい、2台目で画像の表示欠けとして表面化するためです。

DEBUGとALLOWED_HOSTSから固める本番設定の点検手順

Djangoが標準で持つ本番向けの検査を、リリース手順へ組み込みます。

DEBUG=FalseとALLOWED_HOSTSの必須化で最初に落ちる箇所

DEBUG = Falseにした瞬間、ALLOWED_HOSTSの設定が必須になります。既定値は空リストで、空のままなら全リクエストが400 Bad Requestで弾かれます。「本番に上げたら真っ白になった」が最初の関門。値にはブラウザがアクセスするホスト名をすべて入れます。独自ドメインだけでなく、ALBのヘルスチェックが打つホストヘッダも対象です。IPアドレスで飛んでくる構成なら、そのIPを含めるかヘルスチェック用のホスト名をターゲットグループ側で指定してください。なおDEBUG = Trueのまま公開すると、例外画面にソースコード・ローカル変数・設定値が露出します。

SECRET_KEYの外出しとcheck –deployをリリース判定へ組み込む手順

SECRET_KEYはセッションやCSRFトークンの署名に使うため、リポジトリへ置いたままにはできません。公式が示す取り出し方はos.environ["SECRET_KEY"]のような環境変数からの読み込みです。鍵の入れ替えではSECRET_KEY_FALLBACKS(既定値は空リスト)へ旧鍵を残し、既存セッションが即座に無効化されるのを避けます。設定全体の検査はmanage.py check --deployで行い、対象は次の3系統です。

  • Critical settings:SECRET_KEYの強度とDEBUGが有効なままでないか
  • Environment-specific settings:ALLOWED_HOSTS、CACHES、DATABASES、STATIC_ROOT、MEDIA_ROOT
  • HTTPS関連:CSRF_COOKIE_SECUREとSESSION_COOKIE_SECURE(いずれも既定値はFalse)

置き場所はCIのビルド段階と、本番と同じ設定を読ませたデプロイ直前の2箇所。無視する項目はSILENCED_SYSTEM_CHECKSで黙らせ、それ以外が1件でも出たらリリースを止める線引きにします。

CONN_MAX_AGEの設定値とワーカー数から見る接続数の上限

CONN_MAX_AGEはDB接続を再利用する秒数で、公式チェックリストは仮想化環境での効果を挙げ600を例示しています。既定は0、リクエストごとに接続と切断を繰り返す動作です。見落とされるのが接続数の掛け算。接続はワーカープロセスごとに保持されるため、必要数は「ワーカー数 × インスタンス台数」になります。9ワーカーを3台なら27接続で、上限が数十のDBインスタンスではすぐ限界に近づく。台数増の予定があるならPgBouncerのような接続プーラを挟んでください。クエリ側の消費を減らす設計は遅延評価とselect_relatedを踏まえたORMのクエリ設計で扱っています。

ロードバランサ配下でHTTPS判定とCSRFが崩れる箇所の対処

ALBやCloudFrontを前に置くと、Django側は「このリクエストはHTTPだ」と誤認します。ループとCSRF失敗の発生源。

プロキシ配下のHTTPS判定とCSRF_TRUSTED_ORIGINSのスキーム指定

ロードバランサでTLSを終端すると、バックエンドへはHTTPで転送されます。公式ドキュメントの表現では、プロキシが元のリクエストがHTTPSだったかを「swallowing」する状態。request.is_secure()がFalseを返し、HTTPSを要求する設定が噛み合わなくなります。対処はSECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")(既定値はNone)ですが、信じてよいのは外部からのヘッダをプロキシが必ず上書きする構成のときだけ。直接到達できる経路が残っていれば、攻撃者が同じヘッダを付けられる状態になります。フォーム送信で「CSRF verification failed」が出る場合はCSRF_TRUSTED_ORIGINS(既定値は空リスト)の設定漏れで、公式は値にスキームを含める必要があると明記しています。example.comではなくhttps://example.comと書く形です。HTTPS強制を足すならSECURE_SSL_REDIRECT(既定値False)はロードバランサ側と二重に持たず、SECURE_HSTS_SECONDS(既定値0)は公式が「Setting this incorrectly can irreversibly (for some time) break your site.」と警告するとおり300秒程度から段階的に伸ばしてください。

配置先の選定:EC2・コンテナ・マネージドの分岐と2026年の制約

手順記事はEC2にUbuntuを入れる前提ですが、選択肢そのものが2026年時点で動いています。

App Runnerが新規に選べない事実と移行先として案内される構成

コンテナを載せるだけで動くマネージドサービスとして紹介されてきたAWS App Runnerは、開発者ガイドの冒頭に「AWS App Runner is no longer open to new customers.」と掲示されています。既存顧客は通常どおり使えるものの、AWSは「we do not plan to introduce new features」と明記。Pythonランタイムも3.7と3.8のサポートが2025年12月1日で終了しました。公式が移行先として案内するのはAmazon ECS Express Modeです。コンテナイメージと2つのIAMロールを渡して1回API呼び出しをすると、Fargate上のECSサービス、ALB、オートスケーリング、ネットワークが自分のアカウント内に構成されます。追加料金は無く、所要は通常3〜5分。ソースコードからのビルドは行わないため、Dockerfileとイメージのプッシュ工程が先に必要です。

配置先4種の比較と静的ファイル・常駐処理・DB接続の扱いの違い

候補に挙がる4種を、Django固有の3論点で比べます。

配置先 静的ファイル 常駐処理 DB接続の持ち方
EC2単体 nginxが直接配信 systemdで同居 ワーカー数ぶん
ECS Fargate S3かCloudFront 別タスクで分離 タスク数と積算
ECS Express Mode WhiteNoiseかCDN 別サービスが必要 スケール数に依存
App Runner WhiteNoise前提 載せられない プーラ推奨

差が出るのは常駐処理の置き場所です。Celeryワーカーや管理コマンドの定期実行は、EC2ならsystemdのtimerで同じホストに置けますが、コンテナ前提のサービスではWebとは別のタスク定義を用意します。App Runnerは既存顧客でもWeb以外のプロセスを載せられません。

EC2単体で足りる条件とコンテナ化を先に済ませる条件の線引き

EC2単体で足りるのは、ピーク時でも1台に収まり、リリースが月に数回以下で、運用担当が固定され、常駐バッチが同一ホストにあって困らない場合です。この条件でコンテナ基盤を組むと、学習と保守の負担が先に来ます。逆に、環境差分の事故が既に起きている、リリースが週次以上、複数人が同時に触る、将来の台数増が見えている、のどれかならコンテナ化を先に済ませるべきです。移行コストはDockerfileとcollectstaticのビルド時実行、ログの標準出力への統一、ヘルスチェック用URLの追加で収まります。オンプレ資産やVPC設計を含めて決める場面では、AWS・Google Cloud・Azureのインフラ構築支援のように、アプリ側の実装とインフラ設計を同じ窓口で見る体制のほうが、設定の食い違いを持ち越さずに済みます。

受託案件で配置先を決めるときに採用を見送る条件と判断の優先順序

判断の順序を言い切ります。第一に、保守を誰が引き継ぐかで決めること。納品後にクライアント側の担当者が触るなら、ECSのタスク定義とIAMロールを理解してもらう前提が要ります。運用体制が1人でAWSの経験が浅いなら、EC2単体で組んで監視を厚くするほうが事故は減ります。第二に、新規案件でApp Runnerは選びません。新機能の追加予定も無いサービスへ、これから数年動かすシステムを載せる理由がないためです。第三に、Kubernetesは条件を満たさない限り見送ります。マイクロサービスが5本以上ある、複数チームが同じ基盤を共有する、既に運用実績が社内にある、のいずれも無いなら、クラスタの維持だけで保守工数が食われて過剰です。採用の可否を確かめる段階なら、Djangoが向くアプリの4類型と見積もりの見方が先です。

初回リリースの実行順序と502・静的404が出たときの切り分け

実行順序と、最初に踏む症状の切り分けです。

初回リリースでmigrateとcollectstaticを並べる実行順序

順序を固定すると、失敗したときにどこまで進んだかが分かります。基本形は次の7段です。

  1. 依存を入れる(pip install -r requirements.txt)
  2. 本番用の設定モジュールと環境変数が読めているか確認する
  3. manage.py check --deployを通す
  4. manage.py migrateでスキーマを適用する
  5. manage.py collectstatic --noinputで静的ファイルを集める
  6. gunicornを再起動し、ヘルスチェック用のURLで200を確認する
  7. nginxの設定を反映する(nginx -tの後にreload)

マイグレーションを先に置くのは、古いコードが新しいスキーマでも動くよう差分を設計する前提があるからです。カラム削除のように後方互換が壊れる変更では、この順序でも安全になりません。適用順序と巻き戻しの手順はmakemigrationsからの流れと本番適用・巻き戻しの実務で扱っているため、無停止を狙うならそちらと組み合わせてください。

502 Bad Gatewayと静的ファイル404でログを見る順番と切り分け

502はnginxがgunicornに繋がらなかった、または応答を得られなかった合図です。nginxのエラーログ、gunicornのログ、Djangoのログと上流から下流へたどります。Connection refusedならgunicornが起動しておらず、Permission deniedならUnixソケットの権限、upstream timed outならtimeoutで落とされた処理があります。起動しているのに502が続くなら、bindのアドレスとnginxのproxy_pass先の一致を見てください。既定値の127.0.0.1:8000のままコンテナで起動すると外から到達できないため、0.0.0.0へバインドします。管理画面のCSSだけ当たらない場合は、開発者ツールで404か403かを見て、404ならcollectstatic未実行かパス不一致、403なら読み取り権限と切り分けます。

よくある質問

検索の多い5つの質問に、設定値と条件で答えます。

Djangoの本番環境にnginxは必須ですか?

gunicornを既定のsyncワーカーで動かすなら実質的に必須です。公式Designは「Requires a buffering proxy (nginx, HAProxy) for production」と記載しており、遅いクライアントを吸収する層が無いとワーカーが占有されます。ただしALBやCloudFrontが前段にあり静的ファイルをCDNで配信するなら、nginxを別途置く必要はありません。判断軸は「バッファリングする層があるか」です。

runserverを本番で使ってはいけないのはなぜですか?

開発サーバはセキュリティ検証も性能検証も行われていないとDjango公式が明言しています。制約は、同時処理能力が乏しく、TLSを扱えず、静的配信を兼ねる設計である点。DEBUG = Falseでは静的配信も止まり、管理画面のCSSすら返せません。本番ではgunicornやuWSGIを使い、ASGIならuvicornを選びます。

collectstaticはデプロイのどの段階で実行しますか?

コードを配置した後、プロセスを入れ替える前です。EC2へ直接デプロイするなら、依存インストールの後、gunicornの再起動の前に実行します。コンテナで配布する場合はDockerfileのビルド段階で実行してイメージへ含め、起動時には走らせません。ManifestStaticFilesStorageを使うなら参照先の欠けたファイルでこの段階が失敗するため、ビルド時に落とすほうが安全です。

DEBUGをFalseにしたら管理画面のCSSが当たらなくなったのはなぜですか?

DEBUG = TrueのあいだDjangoが肩代わりしていた静的ファイルの配信が止まるからです。本番ではmanage.py collectstaticでSTATIC_ROOTへファイルを集め、その場所をnginxやCDNから配信する構成へ切り替えます。確認するのは、ファイルが実際に集まっているか、Webサーバの設定がそのパスを指しているか、STATIC_URLとパス名が一致しているかの3点です。

Djangoのデプロイ先はEC2とコンテナのどちらを選べばよいですか?

リリース頻度と運用体制で決めます。月数回のリリースで担当者が固定され、1台に収まる規模ならEC2単体で十分です。週次以上のリリース、複数人での同時開発、環境差分による事故、将来の台数増のいずれかならコンテナ化を先に済ませます。なおApp Runnerは新規案件では選べず、ECS Express ModeかECS Fargateが候補になります。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2025.11.04 テックブログ Runpodとは?料金・使い方・支払い方法とGPUクラウドの選び方【2026年10月】
  3. 2026.01.09 コラム 違背実験とは?読み方・意味と具体例、ガーフィンケル原典の数値とエスノメソドロジー
  4. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順

RELATED POSTS 関連記事

目次