- systemdはLinux起動時にPID1として立ち上がり、ネットワークやサービスなどOS全体を管理します。
- 設定はユニットファイルで行い、.serviceで常駐やワンショット実行、.timerで定期実行を制御します。
- 操作はsystemctlコマンドで行い、ファイル変更後はdaemon-reloadが必須です。
- ログはjournalctlで確認でき、ログアウト後も動かすにはloginctl enable-lingerが必要です。
systemctl --user daemon-reload # 変更を反映(必須)
systemctl --user enable --now myapp.timer # タイマーの場合の起動・登録
systemctl --user enable --now myapp.service # 常駐サービスの起動・登録
journalctl --user -u myapp.service -n 30 --no-pager
systemctl --user disable --now myapp.timer # タイマーの停止・解除Code language: CSS (css)
1. systemd とは
Linux を起動すると、カーネルが最初に立ち上げるプロセスがあります。
それが systemd です。
PID(プロセスID)が必ず1番になります。
systemd はその後、ネットワーク、ログ、タイマー、各種サービスといった「OS が動くために必要なもの」を順番に起動していきます。
昔は init というシンプルなプログラムがこの役割を担っていましたが、現在の主要な Linux ディストリビューション(Ubuntu、Debian、Fedora など)はほぼ systemd に移行しています。
systemd が管理する設定ファイルをユニットファイルと呼びます。.service、.timer、.socket などの拡張子を持ちます。
1.1. systemctl とは
systemd を操作するコマンドが systemctl です。
サービスの起動・停止・状態確認・自動起動の設定など、ほぼすべての操作をこれで行います。
systemctl status nginx # 状態確認
systemctl start nginx # 起動
systemctl stop nginx # 停止
systemctl restart nginx # 再起動
systemctl reload nginx # 設定再読込(プロセスは落とさない)
systemctl enable nginx # OS起動時に自動起動するよう登録
systemctl disable nginx # 自動起動を解除
systemctl enable --now nginx # 登録して今すぐ起動も同時に行うCode language: PHP (php)
1.2. システムモードとユーザーモード
systemctl には2つのモードがあります。
| モード | コマンド | 権限 | ユニットファイルの場所 |
|–|-||-|
| システム | systemctl | root | /etc/systemd/system/ |
| ユーザー | systemctl --user | ログインユーザー | ~/.config/systemd/user/ |
nginx や sshd のようにシステム全体で動くサービスはシステムモードです。
自分のスクリプトを自分の権限で動かすだけならユーザーモードで十分です。
以降はユーザーモードを前提にします。
1.3. journalctlでログを確認する
ログは journalctl で見ます。
systemd が管理するサービスのログはすべてここに集まります。
journalctl -u nginx # nginx のログ全件
journalctl -u nginx -f # リアルタイムで流す
journalctl -u nginx -n 50 # 最新50行
journalctl -u nginx --no-pager # ページャーなしで出力(ログが切れない)Code language: PHP (php)
2. .service ファイルの書き方(~/.config/systemd/user/)
ユニットファイルは ~/.config/systemd/user/ に置きます。
2.1. 一度だけ実行するタスク(oneshot)
タイマーと組み合わせてバッチ処理をさせる場合の基本形です。
[Unit]
Description=My batch task
[Service]
Type=oneshot
ExecStart=%h/scripts/mytask.sh
WorkingDirectory=%h/myproject
%h はホームディレクトリに展開される変数です。/home/username を直接書いても動きますが、%h にしておくとユーザー名が変わっても使い回せます。
Type=oneshot は「実行して終わる」タスクに使います。
スクリプトが終了すると systemd はそのサービスを完了扱いにします。
2.2. 常駐させるサービス(restart)
一方、ループして待ち受けるスクリプト・プログラムの場合は、Type=simple にします。
これは、プロセスが起動し続けるサービスに使います。
[Unit]
Description=My watch daemon
[Service]
Type=simple
ExecStart=%h/scripts/mywatch.sh
Restart=always
RestartSec=2
WorkingDirectory=%h/myproject
[Install]
WantedBy=default.targetCode language: JavaScript (javascript)
Restart=always を書くと、スクリプトが落ちたときに自動で再起動します。RestartSec=2 は再起動までの待機秒数です。
2.3. 落とし穴:ログインシェルではない
systemd のサービスとして実行するスクリプトは、ユーザー権限で実行してはいるものの、ログインシェルで起動しているわけではありません。
どういうことかというと、
~/.bashrcや~/.profileが読まれないPATHが/usr/local/bin:/usr/bin:/bin程度しかない- nvm、pyenv、rbenv などでインストールしたコマンドが見つからない
たとえば、PATHが通っていないと処理の途中でコマンドが見つからないことがあります。
すると、終了コード 127 で失敗してしまいます。
ログに status=127/n/a と出たらこれが原因です。
そこで、使いたいコマンドのパスをサービスファイルの「Environment」に直接書きます。
[Service]
Environment=PATH=/home/chii/.nvm/versions/node/v22.0.0/bin:/usr/local/bin:/usr/bin:/bin
Type=oneshot
ExecStart=%h/scripts/mytask.sh
たとえば、nvm のバージョンディレクトリは ls ~/.nvm/versions/node/ で確認できます。
3. .timer ファイルの書き方
タイマーを設定すると、cron の代わりに自動実行するサービスとして使えます。.timer と.service は同じファイル名(base name)にして、セットで動きます。
たとえば、myapp.timer は myapp.service を呼び出します。
[Unit]
Description=Run myapp every 5 minutes
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.targetCode language: JavaScript (javascript)
Persistent=true を付けると、マシンが止まっていた間の実行を再起動後に補完します。
cron にはない機能です。
3.1. OnCalendar の書式
| 書き方 | 意味 |
|---|---|
*:0/5 | 5分おき |
*:0/3 | 3分おき |
hourly | 毎時0分 |
daily | 毎日0時 |
*-*-* 07:00:00 | 毎日7時 |
Mon *-*-* 09:00:00 | 毎週月曜9時 |
書式の詳細は man systemd.time で確認できます。
3.2. OnUnitActiveSec を使う場合の注意
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
この書き方は環境によって active (elapsed) のまま繰り返しが起動しないことがあります。
定期実行には OnCalendar を使うほうが確実です。
というのも、OnUnitActiveSecは、前回実行から次の実行を予約します。
OS起動後でないと、OnBootSecが実行されないので、システムを稼働させたままだと動かないことがあるのです1。
4. 有効化と起動(daemon-reload)
ファイルを作成・編集したあとは必ずこの順番で実行します。
systemctl --user daemon-reload # 変更を反映(必須)
systemctl --user enable --now myapp.timer # タイマーの場合
systemctl --user enable --now myapp.service # 常駐サービスの場合Code language: PHP (php)
daemon-reload を忘れると古い設定のまま動きます。
4.1. 状態確認(systemctl status)
list-timers の NEXT 列が - になっているとタイマーが待機状態ではありません。
ただし LAST に時刻が入っていれば過去に実行されているので、NEXT が - でもログに実行記録があれば正常に動いていることがあります。
# タイマーの一覧(次回・最終実行時刻が出る)
systemctl --user list-timers --all
# サービスの状態
systemctl --user status myapp.service
systemctl --user status myapp.timer
# ログ
journalctl --user -u myapp.service -n 30 --no-pager
journalctl --user -u myapp.service -f # リアルタイムCode language: PHP (php)
4.2. ログアウト後も動かし続ける
デフォルトではログアウトするとユーザーサービスも止まります。
loginctl enable-linger $USERCode language: PHP (php)
このコマンドを一度実行しておくと、ログアウト後もサービスが動き続けます。
常時バックグラウンド処理をさせたい場合に必要です。
5. トラブル対応の流れ
# 1. ログで終了コードとエラーを確認
journalctl --user -u myapp.service -n 30 --no-pager
# 2. よくある終了コードの意味
# 127 コマンドが見つからない(PATH の問題)
# 1 スクリプト内のエラー
# 126 実行権限がない(chmod +x が必要)
# 3. 構文チェック
systemd-analyze verify ~/.config/systemd/user/myapp.service
# 4. 実行権限の確認と付与
ls -la ~/scripts/mytask.sh
chmod +x ~/scripts/mytask.shCode language: PHP (php)- 元の「OnUnitActiveSec」は前回実行からX後に次の回を実施するという設定。で、それだけだと、「最初の1回」を実行する指定がされてないのですね。 –Systemctl Timerが動かないなと思ったら #systemctl – Qiita