開発・技術選定

Django を VPS で本番運用する最小構成 — Nginx + Gunicorn + systemd + Let's Encrypt

個人開発のサービスを「とりあえず動く」から「人に見せられる」状態にするとき、
最初にぶつかるのがデプロイです。ここでは PaaS を使わず、VPS 1台で Django を
本番稼働させる最小構成をまとめます。

全体像

[ブラウザ]
   ↓ HTTPS
Nginx  … TLS終端 / 静的ファイル配信 / リバースプロキシ
   ↓ UNIXソケット
Gunicorn … Django アプリ本体(systemd で常駐)
   ↓
SQLite または PostgreSQL

ポイントは Nginx と Gunicorn を UNIX ソケットでつなぐことです。
TCP ポートを開けずに済むので、外部から直接アプリへ到達される経路が減ります。

Gunicorn を systemd で常駐させる

個人開発でいちばん多い事故が「サーバーを再起動したらサービスが上がってこない」
「プロセスが落ちたまま気づかない」です。nohup で起動していると確実に起こります。
systemd に任せると、この2つが同時に解決します。

[Unit]
Description=My Django App
After=network.target

[Service]
User=www-data
Group=www-data
RuntimeDirectory=gunicorn-myapp
WorkingDirectory=/srv/myapp
ExecStart=/srv/myapp/venv/bin/gunicorn config.wsgi:application \
    --bind unix:/run/gunicorn-myapp/gunicorn.sock \
    --workers 3
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

重要なのは最後の2行です。

  • Restart=on-failure … プロセスが異常終了したら自動で起動し直す
  • WantedBy=multi-user.target … サーバー再起動時に自動で立ち上がる

RuntimeDirectory を指定しておくと、ソケットを置く /run/... を systemd が
権限つきで作ってくれます。手で mkdir すると再起動時に消えて起動に失敗します。

Nginx 側

upstream myapp {
    server unix:/run/gunicorn-myapp/gunicorn.sock fail_timeout=0;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location /static/ { alias /srv/myapp/staticfiles/; expires 30d; }

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_pass http://myapp;
    }
}

X-Forwarded-Proto を渡すのを忘れると、Django 側が「HTTP で来ている」と誤認します。
SECURE_SSL_REDIRECT = True と組み合わせるとリダイレクトループになるので、
settings 側で必ず次を入れておきます。

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

証明書の自動更新で忘れがちなこと

Let's Encrypt は certbot が自動更新してくれますが、更新後に Nginx をリロード
しないと古い証明書を配信し続けます
。certbot の nginx プラグインを使っていない
場合は、更新フックを自分で置く必要があります。

# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh
/usr/sbin/nginx -t && /bin/systemctl reload nginx

まとめ

  • Gunicorn は必ず systemd 管理にする(自動復旧と自動起動が同時に手に入る)
  • Nginx と Gunicorn は UNIX ソケットでつなぐ
  • X-Forwarded-ProtoSECURE_PROXY_SSL_HEADER はセットで入れる
  • 証明書更新後の Nginx リロードをフックで自動化する

この4つを最初に押さえておくと、後から「なぜか落ちている」という時間を大幅に減らせます。