Docker Composeを再起動後に自動起動する方法
Docker Composeを再起動後も自動起動する設定を解説します。restart policyの違い、on-failureがブート後に復旧しない理由、systemd unitが必要なケースを確認できます。
短い答え
Docker Compose のサービスがブート時に起動するには、2 つの条件を同時に満たす必要があります。Docker daemon が system service として有効になっていることと、ファイル内の各サービスに unless-stopped または always の restart policy が設定されていることです。すべてのサービスに restart: unless-stopped を追加し、1 回だけ docker compose up -d を実行すれば、再起動後にコンテナが自動的に起動します。一般的な構成では、これ以外の設定は必要ありません。
起動順序が重要な場合に限り、systemd unit が必要です。たとえば、Docker daemon の起動時点でまだ利用できないマウント済みディスク、VPN インターフェース、ネットワーク共有に依存する stack などです。このケースも実際に発生するため、このガイドの後半で説明します。サービス定義と volume の扱いにまだ慣れていない場合は、まず VPS 上での Docker Compose の基本 を確認してから戻ってきてください。
compose.yaml で再起動ポリシーを設定する
このポリシーは、サービスごとに 1 行で設定します。全体に適用するスイッチはありません。そのため、設定を忘れたサービスは再起動後も停止したままになり、スタック内の他のサービスだけが起動します。
services:
app:
image: nginx:1.27
restart: unless-stopped
ports:
- "8080:80"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: change-me
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:設定を適用し、実行中のコンテナからポリシーを読み戻します。
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)unless-stopped と表示されます。no と表示された場合は、ファイルは編集されていますが、コンテナが再作成されていません。
これは最もよくある失敗です。再起動ポリシーは YAML ファイルではなく、コンテナに保存されます。既存のコンテナに対して compose.yaml を編集しても、コンテナの設定は変わりません。docker compose restart も役に立ちません。これは同じコンテナオブジェクトを停止して起動するだけで、設定を変更しないためです。docker compose up -d だけがファイルと実行中のコンテナを比較し、ポリシーの変更を検出してコンテナを再作成します。
すぐに再作成したくないコンテナでは、ポリシーをその場で変更します。
docker update --restart unless-stopped my-containerYAML ファイルも必ず編集してください。docker update は実行中のコンテナを変更しますが、次回の docker compose up -d ではファイルが読み込まれ、以前の値に戻されます。
再起動値ごとの実際の動作
Docker では 4 つの値が定義されています。値の違いが現れるのは、マシンの再起動時またはデーモンの再起動時だけです。
noはデフォルト値です。どのような状況でも、コンテナは自動的に再起動されません。alwaysは、コンテナが停止するたびに再起動します。手動で停止した場合でも、次回 Docker デーモンが起動すると再び起動します。これは予想外の動作になりがちです。先週、意図的に停止したコンテナが、再起動後に再び実行されています。unless-stoppedはalwaysと同じように動作します。ただし、手動で停止したコンテナは、デーモンの再起動後も停止したままです。保守作業のために、ときどき停止するサービスにはこの値を指定します。on-failureは、ゼロ以外の終了コードで終了した場合にだけコンテナを再起動します。restart: on-failure:3のように、再試行回数の上限を設定できます。
サーバーが稼働している間は常に起動している必要があるスタックでは、unless-stopped が適切なデフォルト値です。停止状態のままになることを防ぎたいコンテナにだけ、always を選択してください。
なぜ再起動されないのか: on-failure は再起動後も維持されない
多くの人は慎重に見えるという理由で on-failure を選びます。しかし、最初の再起動後にすべてのコンテナが停止していることに気付きます。理由は定義にあります。on-failure が反応するのは、コンテナプロセスがエラーコードで終了した場合だけです。
再起動はエラーではありません。ホストのシャットダウン時に systemd が docker.service を停止し、デーモンが各コンテナを意図的に停止します。コンテナは異常終了していないため、再起動ポリシーの対象になりません。起動すると、デーモンは再開が必要なコンテナを確認しますが、正常に停止した on-failure コンテナはその対象に含まれません。そのまま exited 状態になります。
動作は直接確認できます。サービスに restart: on-failure を設定し、docker compose up -d を実行して再起動します。その後、次を実行します。
docker compose ps -aサービスは Exited 状態で、Exited (0) 2 minutes ago のようなステータスとともに表示されます。何も壊れておらず、エラーとして記録される内容もありません。このため、原因の特定が難しくなります。ポリシーは指定どおりに動作しています。
on-failure にも用途はあります。ジョブを実行し、異常終了する可能性があるコンテナで、再試行回数を制限し、再起動ループを防ぎたい場合に適しています。再起動後も長時間稼働するサービスを維持する用途には適していません。
再起動ポリシーは、Docker サービスがブート時に起動する場合にのみ機能します
再起動ポリシーは Docker デーモンによって適用されます。デーモンが起動しなければ、何も適用されません。確認します。
systemctl is-enabled docker
systemctl is-enabled containerdどちらも enabled と表示されるはずです。Docker の公式リポジトリから提供されるパッケージでは、インストール時にこれらが有効になります。そのため、新しいサーバーでは通常この確認に合格します。どちらかが disabled と表示される場合は、修正します。
sudo systemctl enable --now docker containerdここには理解しておくべき注意点があります。Ubuntu には docker.socket も含まれています。これは、Docker API に初めてアクセスがあった時点でデーモンをオンデマンド起動します。docker.socket が有効になっているのを見て、デーモンも起動対象になっていると判断し、メモリを節約するために docker.service を無効にする人がいます。ブート時に API を呼び出すものがなければ、ソケットは一度もアクセスされず、デーモンも起動しません。そのため、最初の docker コマンドを入力するまでコンテナは起動しません。ソケットアクティベーションは、docker.service を有効にする代わりにはなりません。
systemd unit が適している場合
再起動ポリシーには、システム内の他の要素との起動順序を扱う仕組みがありません。daemon が起動すると、可能になり次第コンテナを起動します。stack が別の volume、NFS (network file system) share、または暗号化ディスク上のディレクトリを bind mount している場合、そのパスが存在する前にコンテナが起動することがあります。Docker は mount point に空のディレクトリを作成して、そのディレクトリを使った状態でコンテナを起動します。その結果、データベースがデータなしで起動します。
次のいずれかに該当する場合は、systemd unit を作成してください。stack の起動前に mount、VPN interface、または別の unit が ready になっている必要がある場合。systemctl stop myapp と systemctl start myapp を、システム上の他の service と同じように動作させたい場合。shutdown 時に daemon と一緒に強制終了するのではなく、stack を正常に停止したい場合です。systemd unit に不慣れな場合は、systemd service と timer の作成でファイル形式を詳しく説明しています。
systemd unit の作成
スタックは、home ディレクトリの外にある固定パスへ配置します。/srv/myapp が適しています。ログイン前に起動する unit が /home を読み取る必要はないためです。
/etc/systemd/system/myapp.service を作成します。
[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target有効化して起動します。
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service正常な unit には Active: active (exited) と表示されます。初めて見ると誤りのように見えますが、正しい状態です。Type=oneshot と RemainAfterExit=yes の組み合わせは、unit がコマンドを実行して完了し、systemd が unit を active のまま維持していることを示します。これにより、シャットダウン時に ExecStop が実行されます。
各行には役割があります。Requires=docker.service により、unit は停止したソケットに対して docker compose を実行せず、すぐに失敗します。After= は起動順序を指定します。Requires= だけでは順序を指定できないためです。RequiresMountsFor= により、systemd はそのパスの mount unit を取り込み、マウント完了まで待機します。これが、restart policy ではなく unit を使う理由です。TimeoutStartSec=0 により、大きなイメージを pull している間に systemd が start job を強制終了することを防ぎます。
2 つの仕組みを組み合わせる場合について説明します。Docker のドキュメントは、restart policy とホストの process manager の併用を推奨していません。この警告が対象とするのは、コンテナのプロセス自体を監視し、daemon が同じ処理を行っている間にコンテナを再起動する process manager です。Type=oneshot unit は何も監視しないため、この unit と併用して compose file に restart: unless-stopped を残して問題ありません。むしろ、それが適切な構成です。systemd はブート時の順序を管理し、daemon は午前3時にクラッシュしたコンテナを処理します。
維持する対象がスタックではなく、単純な長時間実行プロセスの場合、unit の構成は異なります。この場合、下位で動作する daemon がないため、systemd 自身の Restart= で監視する必要があります。systemd の背後で dsh をヘッドレス実行する では、専用ユーザーと journal まで含めて、その構成を例示しています。
実際に再起動して確認する
実際のテストに代わるものはありません。systemctl restart docker ではマウントの順序を検証できず、docker compose down に続けて docker compose up -d を実行しても、ブートに関する検証は何もできません。
sudo reboot待機して再接続し、次の順序で確認します。
uptime
systemctl is-active docker
docker compose psuptime で、実際に再起動したマシンを確認していることを検証できます。スタックのディレクトリから実行する docker compose ps では、すべてのサービスが running として表示され、稼働時間がマシンの稼働時間に近くなるはずです。Exited と表示されたサービスを確認します。
起動しなかったものがある場合は、デーモンのログでブート中の時間帯を確認できます。
journalctl -u docker.service -b --no-pager | tail -50unit で管理されるスタックでは、journalctl -u myapp.service -b --no-pager により、ブート時の正確な docker compose 出力を確認できます。イメージの pull 失敗や .env ファイルの欠落も含まれます。スケジュールした再起動は自分で監視するため、監視できない再起動については unit に通知させます。自前でホストする ntfy サーバーを指定した OnFailure= 行により、復帰に失敗したスタックを、数日後に発見するのではなく、push 通知として受け取れます。
自動起動をひそかに妨げる要因
docker compose runで作成したコンテナには、ファイルに定義された restart policy が適用されません。Compose はこれらを一時的なコンテナとして扱います。サービスが restart policy を無視しているように見える場合は、upではなく runで起動していないか確認してください。
volume または env_fileエントリ内の相対パスは、Compose ファイルのディレクトリを基準に解決されます。シェルから実行する場合や、WorkingDirectoryを設定した unit から実行する場合は問題ありません。WorkingDirectoryを設定していない unit から実行すると失敗します。この場合、作業ディレクトリは /になるためです。
Rootless Docker は別のケースです。daemon は user service として実行されます。user service は、そのユーザーの最後のセッションが終了すると停止します。ユーザー用に有効化し、ログイン中のユーザーがいなくても実行を継続できるようにします。
systemctl --user enable docker
sudo loginctl enable-linger $USERenable-lingerがない場合、ログアウトすると rootless daemon が停止し、コンテナも停止します。この状態は、restart policy が壊れている場合とまったく同じように見えます。
最後に、セキュリティ更新の自動適用によって、サーバーが固定時刻に再起動されることがあります。これは、スタックが自動的に復旧する場合に限って有効です。新しいマシンでこの設定を行う作業は、新しい VPS で最初の 10 分間に行う作業に含めます。
FAQ
restart: always と restart: unless-stopped の違いは何ですか?
どちらも、コンテナが自動的に停止した場合は再起動します。違いが出るのは、手動でコンテナを停止した後です。always では、次回 Docker daemon が起動したときにコンテナも再び起動するため、再起動すると手動停止が解除されます。unless-stopped では、daemon がコンテナの意図的な停止を記憶し、そのまま停止状態を維持します。再起動後も停止状態を維持したいコンテナでなければ、unless-stopped を使用します。
restart: unless-stopped を追加したのに、再起動後もコンテナが起動しないのはなぜですか?
ポリシーはファイルではなくコンテナに保存されます。すでに存在するコンテナは、YAML を編集しただけでは更新されません。docker compose up -d を実行して Compose にコンテナを再作成させ、docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) で確認します。出力が no の場合、そのコンテナは編集前に作成されたものです。もう1つの一般的な原因は docker.service が有効になっていないことです。systemctl is-enabled docker で確認できます。
restart ポリシーを使用している場合、systemd unit は必要ですか?
通常は必要ありません。ネットワークだけを必要とする stack であれば、restart ポリシーで十分です。これは多くの stack に当てはまります。外部ディスク、暗号化ボリューム、NFS 共有、VPN インターフェースなど、Docker daemon の起動時にまだ利用できないものへコンテナが依存する場合は、unit を追加します。unit では After= と RequiresMountsFor= による起動順序を指定できますが、restart ポリシーでは指定できません。
次回の再起動後に stack が起動しないよう、完全に停止するにはどうすればよいですか?
unless-stopped では、docker compose stop で十分です。手動で停止したコンテナは、daemon の再起動時に再開されないためです。always では、停止するだけでは不十分で、再起動後にコンテナが戻ります。docker compose down を実行してコンテナを削除するか、先に docker update --restart no my-container でポリシーを変更します。systemd unit が stack を管理している場合は、sudo systemctl disable myapp.service も実行してください。そうしないと、unit が再び起動します。