VPSでプログラムをsystemdサービスとして実行する方法
systemdサービスでプログラムを起動時に自動起動し、クラッシュ時に再起動します。unitの書き方、timerの追加、journalログの確認、権限を制限する設定を解説します。
systemd サービスとは何か、なぜ必要なのか
systemd サービスは、プログラムを実行する方法をサーバーに指示する小さなテキストファイルです。起動時にプログラムを開始し、クラッシュした場合は再起動し、出力をシステムログに送ります。役割はこれだけです。SSH セッションで手動起動したプログラムは、ログアウトするかサーバーが再起動すると終了します。systemd サービスで管理するプログラムは、シェルではなくサーバー自体が管理するため、実行を継続します。
systemd は Ubuntu、Debian、Fedora、および多くの最新 Linux サーバーで使用されている init システムです。最初に起動するプロセスであり、それ以外のすべてを監視します。以前は常にそうだったわけではありません。unit ファイルの役割を理解したら、systemd が以前の init スクリプトに取って代わった経緯も一度読んでおくとよいでしょう。サービスファイルを作成すると、プログラムをこの監視プロセスに委ねることになります。このガイドでは、動作する最小構成の unit、すべての unit にある 3 つのセクション、サービスを有効化してログを読む方法、timer でスケジュール実行する方法、可能な限り少ない権限で実行するための制限方法を説明します。
動作する最小構成のサービス
サービスファイルは /etc/systemd/system/ に置き、.service で終わります。必要な行は数行だけです。/usr/local/bin/myapp にあるプログラム用に作成します。
sudo nano /etc/systemd/system/myapp.service[Unit]
Description=My application
[Service]
ExecStart=/usr/local/bin/myapp
[Install]
WantedBy=multi-user.targetこれで完全に動作する unit になります。ExecStart は実行するコマンドです。WantedBy=multi-user.target はサーバーが通常の multi-user 動作に到達した後に起動することを指定します。これにより、ブート時に起動します。その他はすべて追加設定です。
3 つのセクションと、それぞれの用途
すべての unit ファイルは、角括弧で囲んだセクションに分かれています。service では 3 つのセクションを使用します。
[Unit] では、service とその関係を定義します。最も頻繁に使用する 2 行は次のとおりです。
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription は、systemctl status に表示される人間向けのラベルです。After=network-online.target は、ネットワークが起動するまで systemd がプログラムを起動しないように指定します。ポートで待ち受けるプログラムや、外部への接続を確立するプログラムでは重要です。
[Service] では、プログラムの実行方法を定義します。設定の大部分はここに記述します。
[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=infoUser=myapp により、プログラムは root ではなく、権限を持たないアカウントで実行されます。これは安全性の観点で最も重要な行です。ここには Type= の行がないため、systemd は simple にフォールバックし、ExecStart プロセスがフォアグラウンドで動作し続けるものとして扱います。プログラムがバックグラウンドへ fork する場合は、起動方法に合った 適切な Type= が必要です。指定しないと、実際の daemon がすでに終了していても、unit は active と報告します。Restart=on-failure と RestartSec=5 は、ほとんどの人が service を作成する理由にあたるため、下の独自のセクションで定義します。
[Install] では、service を有効化したときの動作を定義します。
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target は、systemctl enable を実行したときに service をブート処理へ組み込む設定です。[Install] セクションがない場合、service は手動で起動できますが、再起動後に自動起動しません。
有効化して監視する
unit ファイルを書き込むか編集した後は、systemd が変更を読み込めるように再読み込みし、サービスを有効化して起動します。これらは 1 つの手順で実行できます。
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.servicedaemon-reload は忘れられがちな手順です。systemd は unit ファイルをキャッシュするため、再読み込みするまで編集内容は反映されません。enable --now はサービスをブート時に有効化し、同時にすぐ起動します。状態を確認します。
sudo systemctl status myapp.service* myapp.service - My application
Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
Main PID: 4123 (myapp)Active: active (running) と enabled になっていれば問題ありません。プログラムの出力を読むには、この unit だけを対象に journal を表示します。
sudo journalctl -u myapp.service -f-f は、tail -f のように新しい行が追加されるたびに追従して表示します。プログラムが標準出力または標準エラー出力に書き込んだ内容は、追加のログ設定なしでここに記録されます。
失敗時の再起動が、ここで必要な理由です
サービスの主な利点は、プログラムが停止したときに systemd が再起動することです。2 行で設定できます。
[Service]
Restart=on-failure
RestartSec=5Restart=on-failure は、プログラムが 0 以外の終了コードで終了した場合、または SIGKILL や SIGSEGV などのクラッシュシグナルで停止した場合に再起動します。正常終了、または SIGTERM、SIGINT、SIGHUP、SIGPIPE による停止では再起動しません。RestartSec=5 は試行の間隔を 5 秒空けます。そのため、すぐにクラッシュするプログラムが短い間隔で再起動を繰り返すことはありません。プロセスを終了し、systemd が再起動することを監視して確認します。SIGKILL を使用してください。既定の SIGTERM は正常な停止として扱われるため、on-failure ではサービスは再起動しません。
sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service5 秒以内に、ステータスに新しい Main PID が表示され、active (running) も再び表示されます。これがこの機能のすべてです。プログラムを tmux や screen で実行し続けるよりも、サービスを使用する方が優れている理由でもあります。
非特権ユーザーで実行し、セキュリティを強化する
root で実行するサービスは、プログラムが悪用されるとサーバー上であらゆる操作を実行できます。専用ユーザーで実行し、systemd のいくつかのディレクティブで実行範囲を制限します。まず、ログインシェルとホームディレクトリを持たないシステムアカウントを作成します。
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp次に User=myapp を設定し、[Service] にセキュリティ強化用の行を追加します。
[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true各行は、プログラムに不要な機能を無効にします。NoNewPrivileges=true は、setuid バイナリを介した場合でも、プロセスが新しい権限を取得できないようにします。PrivateTmp=true は、他のプロセスから見えない専用の /tmp を割り当てます。ProtectSystem=strict は、ReadWritePaths= で指定した一部のパスを除き、ファイルシステム全体を読み取り専用にします。ProtectHome=true は、/home をプロセスから完全に隠します。これは、サービスをファイアウォールの背後に置く場合と同じ最小権限の考え方です。必要なものだけを与えます。VPS の IPv6 ファイアウォールの抜け穴を塞ぐ方法 に関するガイドを読んでいる場合、これは同じ考え方をホスト上で実施するものです。インターネットに公開するサービスでは、このセキュリティ強化に SSH の前段に Fail2ban を配置する方法 とデフォルト拒否のファイアウォールを組み合わせます。
これらを手作業で入力してディレクティブを間違えて覚える代わりに、完全なセキュリティ強化済み unit を生成してコピーします。
タイマー: 最新の cron
systemd timer はスケジュールに従ってサービスを実行します。cron job に代わる最新の仕組みです。タイマーは2つのファイルで構成されます。処理を実行する .service と、実行時刻を定義する .timer です。毎日 3am にバックアップを実行するとします。サービスは処理を1回実行して終了します。
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shType=oneshot は、プログラムが常駐せず、実行して終了することを systemd に伝えます。タイマーで実行時刻を定義します。
# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetOnCalendar=*-*-* 03:00:00 は毎日 3am を意味します。systemd-analyze calendar "*-*-* 03:00:00" を使用すると、任意のカレンダー式をテストできます。式を解析できることを確認し、次回以降の実行時刻を表示します。Persistent=true を指定すると、3am の時点でサーバーが停止していた場合、サーバーの復旧後すぐに未実行の job を実行します。cron ではこの動作はできません。タイマーは timers.target で有効化し、multi-user.target ではない点に注意してください。サービスではなく、タイマーを有効化します。
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers は、次回と前回の実行時刻を含むすべてのタイマーを表示します。これにより、job の次回実行時刻をすぐに確認できます。上記の generator は、timer mode を有効にすると、対応する .service と .timer を自動的に作成します。cron line と比較すると、タイマーでは journal に実行ログが記録され、サービスと同じ hardening directive を使用でき、Persistent=true が提供する未実行処理のキャッチアップも利用できます。単純な job には cron で十分です。ただし、重要な job にはタイマーのほうが適しています。
FAQ
systemd service と cron job の違いは何ですか?
service は長時間実行するプログラムを維持します。ブート時に起動し、失敗時に再起動し、journal にログを記録します。cron job はスケジュールに従って短いコマンドを実行し、その後終了します。スケジュール実行に加えて journal ログ、hardening、実行できなかった処理の catch-up も必要な場合は、.timer schedule と oneshot service を組み合わせる systemd timer を使用します。ほとんどのサーバータスクでは cron の代わりになります。
systemd service file はどこに配置しますか?
独自の unit は /etc/systemd/system/ に配置し、名前の末尾を .service にします。このディレクトリは管理者が追加する unit 用で、/lib/systemd/system/ にあるパッケージ提供の unit より優先されます。ファイルを作成または編集した後、sudo systemctl daemon-reload を実行して systemd に変更を読み込ませます。
service がクラッシュした場合に再起動するにはどうすればよいですか?
[Service] section に Restart=on-failure と RestartSec=5 を追加し、sudo systemctl daemon-reload を実行してから service を再起動します。systemd は、プログラムが non-zero code で終了した場合、またはクラッシュシグナルで終了した場合に再起動します。再試行の間隔は5秒です。sudo systemctl kill -s SIGKILL myapp.service でテストします。デフォルトのシグナルである SIGTERM は正常な停止として扱われ、on-failure は実行されません。数秒以内に新しい PID が表示されることを systemctl status で確認します。
systemd service を root 以外のユーザーで実行するにはどうすればよいですか?
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp で system account を作成し、[Service] section に User=myapp を追加します。プロセスに必要な最小限のアクセス権だけを与えるため、NoNewPrivileges=true、PrivateTmp=true、ProtectSystem=strict も追加します。非特権ユーザーで実行することは、service の安全性を高めるうえで最も重要な変更です。
service の起動に失敗したのはなぜですか?
概要を確認するには systemctl status myapp.service、完全な出力を確認するには journalctl -u myapp.service を実行します。よくある原因は、ExecStart のパスが誤っていること、WorkingDirectory がないこと、User= がファイルを読み取れないために権限エラーになること、編集後に sudo systemctl daemon-reload を忘れていることです。journal にはプログラム自身のエラーメッセージが記録されます。通常、このメッセージに問題の内容が直接示されます。