SSD Nodes Learn
ガイド Matt Connor著者 Matt Connor ・更新日 2026-07-24

VPSでプログラムをsystemdサービスとして動かす方法

VPS上のプログラムをsystemdで常時稼働させる手順を解説します。起動時の自動開始やクラッシュ時の再起動、journalでのログ管理、timerによる定期実行、セキュリティを高める権限設定まで網羅しています。SSHログアウト後もプロセスを維持したい場合に必須の知識です。

systemd serviceの概要と必要性

systemd serviceは、プログラムの実行方法を定義する小さなテキストファイルです。起動時にプログラムを開始する、クラッシュ時に再起動する、出力をシステムログに送る、といった役割を担います。SSHセッションで手動で起動したプログラムは、ログアウトやサーバーの再起動によって終了します。一方、systemd serviceで管理されたプログラムは、シェルではなくシステム自体が管理するため、継続して動作します。

systemdは、Ubuntu、Debian、Fedora、およびほとんどの最新Linuxサーバーにおけるinit systemです。最初に起動するプロセスであり、他のすべてのプロセスを監視します。serviceファイルを記述することで、プログラムをこの監視プロセスに委ねることができます。本ガイドでは、最小構成のユニット、すべてのユニットに共通する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

これで完全な動作ユニットとなります。実行コマンドは ExecStart です。WantedBy=multi-user.target は、サーバーがマルチユーザー動作モードに移行した後に起動することを意味します。これにより、起動時にサービスが開始されます。それ以外の設定は、必要に応じて調整してください。

3つのセクションとその用途

すべての unit file は、角括弧で囲まれたセクションに分かれています。service では3つのセクションを使用します。

[Unit] は、service とその関係性を記述します。最も頻繁に使用する2行は以下の通りです:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description は、systemctl status で表示される人間向けのラベルです。After=network-online.target は、ネットワークが起動するまでプログラムを開始しないよう systemd に指示します。これは、port を bind するものや、外部へ connection を作成するものにとって重要です。

[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=info

User=myapp は、プログラムを root ではなく権限のない account で実行します。これは、セキュリティにおいて最も重要な行です。Restart=on-failureRestartSec=5 は、個別のセクションとして以下に記載します。これらは、多くのユーザーが service を作成する主な理由です。

[Install] は、service を enable したときに実行される内容です:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target は、systemctl enable を実行した際に service を boot 時の起動プロセスに組み込むための設定です。[Install] セクションがない場合、service を手動で起動することはできますが、reboot 後に自動で起動することはありません。

有効化して動作を確認する

unit file を作成または編集した後は、変更を反映させるために systemd を reload してください。その後、以下のコマンドでサービスの enable と start を同時に行います。

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload は忘れがちな手順です。systemd は unit file をキャッシュするため、reload しない限り編集内容は反映されません。enable --now を使うと、サービスの自動起動設定(enable)と即時起動(start)を同時に実行できます。以下のコマンドで確認してください。

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 です。プログラムの出力内容を確認するには、journal でこの unit のログのみを表示します。

sudo journalctl -u myapp.service -f

-f は、tail -f のように新しい行が追加されるたびに表示を更新します。プログラムが standard output または standard error に書き込んだ内容は、ログ設定を行わなくてもすべてここに記録されます。

失敗時の再起動、この記事の目的

service の主な利点は、プログラムが終了した際に systemd が自動的に再起動することです。以下の2行で設定できます。

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure は、終了コードが 0 以外の場合、または SIGKILL や SIGSEGV などのクラッシュ信号で終了した場合にプログラムを再起動します。正常な終了、または SIGTERM、SIGINT、SIGHUP、SIGPIPE による停止では再起動されません。RestartSec=5 は試行の間に 5 秒間の待機時間を設けます。これにより、即座にクラッシュするプログラムが無限ループに陥るのを防ぎます。プロセスを kill して systemd が再起動することを確認してください。SIGKILL を使用してください。デフォルトの SIGTERM は正常な停止とみなされるため、on-failure ではサービスは再起動されません。

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

5 秒以内に、status に新しい Main PIDactive (running) が表示されます。これがこの機能の全容です。これが、プログラムを tmuxscreen で実行し続けるよりも service を使うべき理由です。

非特権ユーザーとして実行し、要塞化する

プログラムが脆弱性を突かれた場合、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 ファイルを生成してコピーしてください。

Toolsystemd service and timer generator

Timers: modern cron の代わりとなる機能

systemd timer は、スケジュールに基づいて service を実行します。これは cron job の現代的な代替手段です。timer は 2 つのファイルで構成されます。処理を行う .timer と、実行タイミングを指定する .service です。例えば、毎日午前 3 時に backup を実行したい場合を考えます。service はタスクを 1 回実行して終了します。

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot は、プログラムが常駐せず、実行後に終了することを systemd に伝えます。timer はその実行をスケジュールします。

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 は、毎日午前 3 時を意味します。systemd-analyze calendar "*-*-* 03:00:00" を使うと、calendar expression のテストが可能です。正しく解析され、次回の実行時刻が表示されるか確認できます。Persistent=true は、午前 3 時にサーバーの電源が切れていた場合、起動直後に実行漏れしたジョブを実行します。これは cron では不可能です。timer は multi-user.target ではなく timers.target で有効化(enable)することに注意してください。service ではなく、timer を有効化します。

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers を実行すると、すべての timer の次回および前回実行時刻が表示されます。これにより、次回の実行タイミングを即座に確認できます。上記の generator は、timer mode を有効にすると、対になる .service.timer を自動生成します。cron と比較して、timer は journal に実際のログを残せます。また、他の service と同様の hardening 指令が使用でき、Persistent=true による実行漏れの補完も可能です。単純なジョブには cron でも十分ですが、重要なジョブには timer が適しています。

FAQ

systemd service と cron job の違いは何ですか?

service はプログラムを常時実行させます。起動時に開始され、失敗時に再起動し、journal にログを記録します。一方、cron job はスケジュールに従って短いコマンドを実行し、終了します。スケジュール実行が必要で、かつ journal ログ、hardening、実行漏れのリカバリ機能も必要な場合は、systemd timer を使用してください。これは .timer スケジュールと oneshot service を組み合わせたもので、ほとんどのサーバータスクにおいて cron の代わりとなります。

systemd service ファイルはどこに置くべきですか?

独自の unit ファイルは、名前の末尾を .service にして /etc/systemd/system/ に配置してください。このディレクトリは管理者が追加する unit 用であり、/lib/systemd/system/ に含まれるパッケージの unit よりも優先されます。ファイルを新規作成または編集した後は、sudo systemctl daemon-reload を実行して systemd に変更を反映させてください。

サービスがクラッシュしたときに再起動させるにはどうすればよいですか?

[Service] セクションに Restart=on-failureRestartSec=5 を追加し、sudo systemctl daemon-reload を実行してからサービスを再起動してください。プログラムが 0 以外の終了コードで終了するか、クラッシュ信号で停止した場合、systemd は再起動を試みます。試行の間には 5 秒間の待機時間があります。sudo systemctl kill -s SIGKILL myapp.service でテストしてください。デフォルトの信号である SIGTERM は正常な停止とみなされるため、on-failure はトリガーされません。数秒以内に systemctl status に新しい PID が表示されることを確認してください。

root 以外のユーザーとして systemd service を実行するにはどうすればよいですか?

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp でシステムアカウントを作成し、[Service] セクションに User=myapp を追加してください。また、プロセスに必要な最小限の権限のみを与えるために、NoNewPrivileges=truePrivateTmp=trueProtectSystem=strict を追加してください。権限のないユーザーとして実行することは、サービスの安全性を高める上で最も重要な変更です。

なぜサービスが起動に失敗したのですか?

概要を確認するには systemctl status myapp.service を、詳細な出力を確認するには journalctl -u myapp.service を実行してください。主な原因は、ExecStart のパス指定ミス、WorkingDirectory の不足、User= によるファイル読み取り権限エラー、または編集後の sudo systemctl daemon-reload の忘れです。journal にプログラム自身のエラーメッセージが表示されます。通常、そこに問題の内容が直接記載されています。