systemdサービスを自動起動させる設定手順|ユニットファイルから有効化まで

systemdサービスを自動起動させる設定手順|ユニットファイルから有効化まで

※本記事にはプロモーション(広告・アフィリエイトリンク)を含みます。

Linuxで開発したWebアプリをサーバーに常駐させたい。でも毎回手動で起動するのは手間だし、サーバーが再起動したら止まってしまう。この記事では、自宅LinuxサーバーでNext.jsアプリをsystemdサービス化して常時稼働させた手順を、ユニットファイルの書き方からsystemctlコマンドまで再現できる形でまとめます。

systemdサービスにすると何が変わるか

systemdはLinuxのinitシステムで、サービスの起動・停止・再起動・自動起動を管理します。nohup &でのバックグラウンド実行と比較すると、違いは明確です。

機能 nohup & systemd
プロセス停止後の自動再起動 ✗ ✓(Restart=always 等)
OS起動時の自動起動 ✗ ✓(systemctl enable)
ログの一元管理 ✗ ✓(journalctl)
起動順序の制御 ✗ ✓(After=network.target 等)

私が運用しているECサイト(Next.js + Prisma + SQLite)も、この仕組みでLinuxサーバーに常駐させています。

ユニットファイルの書き方

systemdのサービス定義は.service拡張子のテキストファイルで記述します。通常は /etc/systemd/system/ に置きます。

配置先 用途
/etc/systemd/system/ システム管理者が作成するサービス
/usr/lib/systemd/system/ パッケージが提供するサービス(上書き非推奨)
~/.config/systemd/user/ ユーザー単位のサービス(--user オプション)

基本的な構造は以下のとおりです。

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

[Service]
Type=simple
User=myuser
Group=myuser
WorkingDirectory=/home/myuser/myapp
ExecStart=/usr/bin/node /home/myuser/myapp/server.js
Restart=always
RestartSec=5
Environment="NODE_ENV=production"

[Install]
WantedBy=multi-user.target

[Unit] — 起動順序と依存関係

After=network.target は「ネットワークが利用可能になった後に起動する」という指定です。Requires= と混同しやすいため、違いを押さえておきます。

ディレクティブ 意味
After=A A が起動完了してから自分を起動する(順序のみ)
Requires=A A が必須。A が止まると自分も止まる
Wants=A A を推奨。A が失敗しても自分は起動する

ほとんどのWebアプリは After=network.target だけで十分です。

[Service] — 実行設定の核心

Type

Node.jsアプリのようにフォアグラウンドで動き続けるプロセスは Type=simple(デフォルト)を使います。デーモン化(fork)するプロセスは Type=forking を指定します。

User と WorkingDirectory

User=kxkxk
Group=kxkxk
WorkingDirectory=/home/kxkxk/ec-site

root で動かすのはセキュリティ上避けます。WorkingDirectory を省略するとルートディレクトリが作業ディレクトリになり、相対パスの読み込みが失敗します。

ExecStart

ExecStart=/home/kxkxk/.nvm/versions/node/v20.11.0/bin/node \
  /home/kxkxk/ec-site/node_modules/.bin/next \
  start --port 3000

ExecStart はフルパスで書きます。~ は展開されず、$HOME も使えません。which node や which next で確認したフルパスを記入してください。nvm を使っている場合はバージョン番号込みのパスになるため注意が必要です。

Restart ポリシー

Restart=always      # 終了理由を問わず再起動
Restart=on-failure  # 異常終了時のみ再起動
RestartSec=5        # 再起動までの待機秒数

本番運用では Restart=always が安全です。意図しないクラッシュからも自動復帰します。

環境変数

Environment="NODE_ENV=production"
Environment="PORT=3000"
EnvironmentFile=/home/kxkxk/ec-site/.env

Environment は1行に1変数。.env ファイルを使う場合は EnvironmentFile で指定します。ただし指定したファイルが存在しないとサービス起動自体が失敗するため、ファイルの存在を先に確認してから使ってください。

[Install] — 自動起動の設定

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target はマルチユーザーモード(ランレベル3相当)で起動することを意味します。GUIなしのサーバー用途ではこれを指定します。この記述があることで systemctl enable が機能します。記述を忘れると enable を実行しても自動起動が登録されません。

systemctl で有効化・起動する

ユニットファイルを作成したら、以下の4ステップで有効化します。

# 1. ユニットファイルを配置
sudo nano /etc/systemd/system/ec-site.service

# 2. systemd に変更を反映
sudo systemctl daemon-reload

# 3. OS起動時の自動起動を有効化
sudo systemctl enable ec-site

# 4. 今すぐ起動
sudo systemctl start ec-site

# 5. 状態確認
sudo systemctl status ec-site

daemon-reload はユニットファイルを変更するたびに実行が必要です。忘れると古い設定が使われ続けます。

enable と start は独立した操作です。enable だけでは現在のセッションでは起動しません。初回は両方実行してください。

よく使うコマンドをまとめます。

sudo systemctl stop ec-site       # 停止
sudo systemctl restart ec-site    # 再起動
sudo systemctl disable ec-site    # 自動起動を無効化
sudo systemctl is-active ec-site  # 動作中か確認(active / inactive)
sudo systemctl is-enabled ec-site # 自動起動が有効か確認(enabled / disabled)

ユニットファイルを編集した後に設定を反映させる場合は daemon-reload → restart の順に実行します。restart だけでは新しいユニットファイルが読み込まれないことがあります(環境によって異なります)。

ログの確認とトラブルシューティング

systemdはサービスの標準出力・標準エラーを自動的に journald に収集します。syslog を設定しなくても、起動からの全ログが保存されます。

# 最新ログを表示
sudo journalctl -u ec-site

# リアルタイムで追跡
sudo journalctl -u ec-site -f

# 直近50行
sudo journalctl -u ec-site -n 50

# 今日のログだけ
sudo journalctl -u ec-site --since today

サービスが起動しない場合、journalctl の出力が最初の手がかりです。よくある原因を整理します。

症状 原因候補
ExecStart: No such file or directory パスのtypo、~ や $HOME の使用
Permission denied User がファイル・ディレクトリを読めない
Start request repeated too quickly 起動直後にクラッシュを繰り返している
Failed to load environment file EnvironmentFile で指定したファイルが存在しない

Start request repeated too quickly が出た場合は RestartSec=10 のように待機時間を伸ばすことで、ログを読む時間を確保できます。アプリ側のエラーログがジャーナルに残るため、そこから原因を特定します。

まとめ

systemdサービス化の要点を整理します。

  • ユニットファイルは /etc/systemd/system/サービス名.service に置く
  • ExecStart はフルパスで書き、~ や $HOME は使わない
  • ファイル変更後は daemon-reload を必ず実行する
  • enable と start はセットで実行する(役割が異なる)
  • トラブル時は journalctl -u サービス名 -n 50 が最初の一手

次のステップとして、systemctl status の出力が Active: active (running) になっていることを確かめてください。その後は Cloudflare Tunnel と組み合わせることで、ポート開放なしに外部から安全にアクセスできる構成も作れます。

よくある質問

systemctl enable したのにOS再起動後にサービスが起動しない

ユニットファイルの [Install] セクションに「WantedBy=multi-user.target」が書かれているか確認してください。この記述がないと enable を実行しても自動起動が登録されません。

ExecStart に ~ や $HOME を書いたらエラーになった

ExecStart はシェル経由で実行されないため、~ や $HOME は展開されません。「which node」等で確認したフルパスを直接記述してください。nvm 使用時はバージョン番号込みのパスになります。

サービスファイルを編集したのに変更が反映されない

ユニットファイルを変更した後は必ず「sudo systemctl daemon-reload」を実行してください。この手順を省略すると、systemd は古い設定を参照し続けます。その後 restart で再起動します。

Node.js アプリで EnvironmentFile を使うとサービスが起動しない

EnvironmentFile に指定したファイルが存在しないとサービス起動自体が失敗します。.env ファイルがサーバー上に配置済みか、パスにタイポがないかを確認してください。