前の記事では、VPS を借りたら最初にやることを、チェックリストにまとめました。今回は、サーバーでプログラムを動かし続けるための systemd を学びます。
もし、VPS で Python のスクリプトを nohup で動かしていたら…。VPS 会社のメンテナンスでサーバーが再起動されると、スクリプトは止まったままになります。しかも、自分で見に行くまで気づけません。「サーバーが起動したら、自動でまた動き出してほしい」をかなえてくれるのが systemd です。
この記事では、自分のパソコンで、練習用のサービスを1つ作って、起動・停止・自動起動の設定までを試します。最後にきれいに消すので、安心して試してください。
- systemd とサービス(unit)とは何か
-
systemctl status・start・stop・restartの使い方 - 自分のスクリプトをサービスにする
.serviceファイルの書き方 -
enable(自動起動)とstart(今すぐ起動)のちがい - 止まったときに自動で立ち上げ直す
Restart=の考え方
- シェルスクリプトを作って実行できる(
chmod +x) -
nohupやtmux(=ターミナルの作業画面を残したまま切りはなし、あとでまた戻れる道具)で、プログラムを動かし続けたことがある -
sudoとnanoを使える
まだの方は、前の記事「VPSを借りたら最初にやること|ユーザー作成・SSHの強化・ufwのチェックリスト【初心者向け】」と、Lv3 の nohup・tmux の記事から読むのがおすすめです。
- 環境変数:シェルが覚えていて、そこから動かすプログラムにも渡される「名前つきの設定値」(
PATHやHOMEなど)。自分で作るときはexportをつけます。つけないものは「シェル変数」で、今のシェルの中だけで使えます - フルパス(絶対パス):
/home/kaito/memo.txtのように、いちばん上の/から書いた場所 - VPS:大きな物理サーバーを区切って作った、自分専用の仮想サーバーを、月額などで借りられるサービス。root 権限で自由に設定できます
- スクリプト:やりたい処理を順番に書いておき、まとめて実行できるファイル(小さなプログラム)
用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。
結論:start は「今」、enable は「次の起動から」
| コマンド | 意味 |
|---|---|
systemctl status 名前 | 動いているか・最近のログを見る |
sudo systemctl start 名前 | 今すぐ起動する |
sudo systemctl stop 名前 | 今すぐ止める |
sudo systemctl restart 名前 | 止めてから起動し直す |
sudo systemctl enable 名前 | パソコン(サーバー)の起動時に自動で起動する |
sudo systemctl disable 名前 | 自動起動をやめる |
sudo systemctl daemon-reload | .service ファイルを書きかえたら読み直す |
準備:すでに動いているサービスを見てみる
Linux では、たくさんのサービスが systemd に見守られながら動いています。まずは cron(定期実行のサービス)の様子を見てみましょう。
- 1
- 2
- 3
systemctl status cron --no-pager
systemctl is-active cron
systemctl is-enabled cronここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。
(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
kaito@demo:~/linux-practice$ systemctl status cron --no-pager
● cron.service - Regular background program processing daemon
Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-11-01 03:40:05 JST; 2min 41s ago
Invocation: de820a05982e426488f91755e510e9b9
Docs: man:cron(8)
Main PID: 131 (cron)
Tasks: 1 (limit: 19197)
Memory: 364K (peak: 1.7M)
CPU: 4ms
CGroup: /system.slice/cron.service
└─131 /usr/sbin/cron -f
Warning: some journal files were not opened due to insufficient permissions.
kaito@demo:~/linux-practice$ systemctl is-active cron
active
kaito@demo:~/linux-practice$ systemctl is-enabled cron
enabled検証環境(Debian 13)では、cron は最初から入っていて、動いていました。最後の Warning: some journal files were not opened due to insufficient permissions. は、「権限が足りないので、一部のログ(記録)は開けませんでした」という意味です。cron は root(システム)として動いているため、一般ユーザーの kaito にはそのログが見えず、ログの数行の代わりにこの注意が出ています。sudo systemctl status cron --no-pager のように sudo をつけると、ログも表示されます。
--no-pager は、結果を less で開かずにそのまま表示するオプションです。Loaded の行の enabled は「自動起動する設定」、Active の行の active (running) は「今動いている」という意味です。
動いているサービスの一覧も見られます。
- 1
systemctl list-units --type=service --state=running --no-pager(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
kaito@demo:~/linux-practice$ systemctl list-units --type=service --state=running --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
console-getty.service loaded active running Console Getty
cron.service loaded active running Regular background program processing daemon
dbus.service loaded active running D-Bus System Message Bus
exim4.service loaded active running exim Mail Transport Agent
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running User Login Management
user@1000.service loaded active running User Manager for UID 1000
Legend: LOAD → Reflects whether the unit definition was properly loaded.
ACTIVE → The high-level unit activation state, i.e. generalization of SUB.
SUB → The low-level unit activation state, values depend on unit type.
7 loaded units listed.どのサービスが動いているかは、パソコンによってちがいます。最後の Legend は、それぞれの列の意味の説明です。
STEP 1:サービスにするスクリプトを作る
練習用に、10秒ごとに時刻を表示し続けるスクリプトを作ります。
- 1
- 2
- 3
mkdir -p ~/linux-practice/systemd-test
cd ~/linux-practice/systemd-test
nano hello-loop.sh次の内容を書いて保存します。
- 1
- 2
- 3
- 4
- 5
#!/bin/bash
while true; do
echo "kaito-hello: $(date '+%H:%M:%S') に動いています"
sleep 10
done- 1
- 2
- 3
chmod +x hello-loop.sh
ls -l hello-loop.sh
pwd(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
kaito@demo:~/linux-practice$ mkdir -p ~/linux-practice/systemd-test
kaito@demo:~/linux-practice$ cd ~/linux-practice/systemd-test
kaito@demo:~/linux-practice/systemd-test$ nano hello-loop.sh
kaito@demo:~/linux-practice/systemd-test$ chmod +x hello-loop.sh
kaito@demo:~/linux-practice/systemd-test$ ls -l hello-loop.sh
-rwxrwxr-x 1 kaito kaito 107 Nov 1 03:43 hello-loop.sh
kaito@demo:~/linux-practice/systemd-test$ pwd
/home/kaito/linux-practice/systemd-testx がついていれば、実行できます。検証環境では -rwxrwxr-x でしたが、-rwxr-xr-x になるパソコンもあります(新しいファイルにつく、最初のパーミッションの設定のちがいです)。
pwd の結果は、次の .service ファイルで使うので控えておきましょう。
STEP 2:.service ファイルを作る
自分で作るサービスの設定ファイルは、/etc/systemd/system/ に置きます。システムの場所なので sudo が必要です。名前は、ほかと重ならないように kaito-hello.service にします。
- 1
sudo nano /etc/systemd/system/kaito-hello.service次の内容を書いて保存します。User= と ExecStart= は、自分のユーザー名とパスに合わせてください。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
[Unit]
Description=kaito の練習用サービス
[Service]
User=kaito
ExecStart=/home/kaito/linux-practice/systemd-test/hello-loop.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target| 書く場所 | 意味 |
|---|---|
[Unit] の Description | サービスの説明(status に表示される) |
[Service] の User | どのユーザーとして動かすか(root にしない) |
ExecStart | 動かすコマンド(絶対パスで書く) |
Restart=on-failure | エラーで止まったら、自動で起動し直す |
[Install] の WantedBy | enable したとき、ふつうの起動の流れに加える |
Restart=on-failure の「エラーで止まったら」は、スクリプトがエラーの終了コード(0 以外。シェルスクリプトの記事で見た $?)で終わったときや、強制的に止められたときのことです。systemctl stop で自分から止めたときや、スクリプトが最後まで進んで 0 で終わったときは、起動し直しません。止めたはずのサービスが勝手に動き出すことはないので、安心してください。
ファイルを作ったら、systemd に読み直してもらいます。
- 1
- 2
sudo systemctl daemon-reload
systemctl status kaito-hello --no-pager(例)
- 1
- 2
- 3
- 4
- 5
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl daemon-reload
kaito@demo:~/linux-practice/systemd-test$ systemctl status kaito-hello --no-pager
○ kaito-hello.service - kaito の練習用サービス
Loaded: loaded (/etc/systemd/system/kaito-hello.service; disabled; preset: enabled)
Active: inactive (dead)disabled は「自動起動しない」、inactive (dead) は「今は止まっている」という意味です。Description に書いた日本語も、そのまま表示されます。
STEP 3:start と stop を試す
- 1
- 2
sudo systemctl start kaito-hello
systemctl status kaito-hello --no-pager(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl start kaito-hello
kaito@demo:~/linux-practice/systemd-test$ systemctl status kaito-hello --no-pager
● kaito-hello.service - kaito の練習用サービス
Loaded: loaded (/etc/systemd/system/kaito-hello.service; disabled; preset: enabled)
Active: active (running) since Sun 2026-11-01 03:43:19 JST; 26s ago
Invocation: ac16cea9b91e443a9021fe2d318156ca
Main PID: 1851 (hello-loop.sh)
Tasks: 2 (limit: 19197)
Memory: 604K (peak: 1.8M)
CPU: 14ms
CGroup: /system.slice/kaito-hello.service
├─1851 /bin/bash /home/kaito/linux-practice/systemd-test/hello-loop.sh
└─1857 sleep 10
Nov 01 03:43:19 demo hello-loop.sh[1851]: kaito-hello: 03:43:19 に動いています
Nov 01 03:43:29 demo hello-loop.sh[1851]: kaito-hello: 03:43:29 に動いています
Nov 01 03:43:39 demo hello-loop.sh[1851]: kaito-hello: 03:43:39 に動いていますCGroup の下には、サービスの中で動いているプログラム(スクリプトと、その中の sleep 10)が並びます。最後の3行が、スクリプトの echo の表示です。10秒おきに増えていきます。
echo で表示した内容は、systemd がログとして預かっています。ログだけを見るときは journalctl(=systemd が集めたログを見るコマンド)を使います(くわしくは Lv4 のログの記事で)。
- 1
journalctl -u kaito-hello -n 5 --no-pager(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
kaito@demo:~/linux-practice/systemd-test$ journalctl -u kaito-hello -n 5 --no-pager
Hint: You are currently not seeing messages from other users and the system.
Users in groups 'adm', 'systemd-journal' can see all messages.
Pass -q to turn off this notice.
Nov 01 03:43:19 demo hello-loop.sh[1851]: kaito-hello: 03:43:19 に動いています
Nov 01 03:43:29 demo hello-loop.sh[1851]: kaito-hello: 03:43:29 に動いています
Nov 01 03:43:39 demo hello-loop.sh[1851]: kaito-hello: 03:43:39 に動いています一般ユーザーで journalctl を使うと、最初に Hint: から始まる3行が出ることがあります。「ほかのユーザーやシステムのログは見えていません。adm か systemd-journal グループ(=ユーザーをまとめたチーム)に入っているユーザーなら全部見られます。-q をつけるとこの表示を消せます」という意味です。今回の kaito-hello は User=kaito で動かしているので、kaito のままでもログを見られます。このあとの記事でも出てきますが、気にせず読み進めてください。
検証では、起動してから約25秒後に見たので、ログは3行でした(-n 5 は「最大5行」という意味です)。
止めて、止まったことを確かめます。
- 1
- 2
sudo systemctl stop kaito-hello
systemctl is-active kaito-hello(例)
- 1
- 2
- 3
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl stop kaito-hello
kaito@demo:~/linux-practice/systemd-test$ systemctl is-active kaito-hello
inactiveSTEP 4:enable で自動起動にする
enable は「次にパソコンを起動したときから、自動で起動する」設定です。今すぐは起動しないのがポイントです。
- 1
- 2
- 3
sudo systemctl enable kaito-hello
systemctl is-enabled kaito-hello
systemctl is-active kaito-hello(例)
- 1
- 2
- 3
- 4
- 5
- 6
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl enable kaito-hello
Created symlink '/etc/systemd/system/multi-user.target.wants/kaito-hello.service' → '/etc/systemd/system/kaito-hello.service'.
kaito@demo:~/linux-practice/systemd-test$ systemctl is-enabled kaito-hello
enabled
kaito@demo:~/linux-practice/systemd-test$ systemctl is-active kaito-hello
inactive「自動起動にして、今すぐ起動もする」は、--now をつけると1回でできます。
- 1
- 2
sudo systemctl enable --now kaito-hello
systemctl is-active kaito-hello(例)
- 1
- 2
- 3
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl enable --now kaito-hello
kaito@demo:~/linux-practice/systemd-test$ systemctl is-active kaito-hello
activeこの時点ではすでに enable 済みなので、enable --now は何も表示せず、今すぐの起動だけが行われました。まだ enable していないサービスなら、Created symlink … の行も表示されます。
本当に再起動後に動き出すかは、パソコンを再起動して systemctl status kaito-hello を見れば確かめられます。再起動してよいタイミングで、試してみてください。
sudo systemctl stop ssh や、ネットワーク関係のサービスを止めると、SSH でつないで作業している場合はその場で切断され、戻れなくなります。disable すると、次の起動からも動かなくなります。この記事で止めたり消したりするのは、自分で作った kaito-hello だけにしてください。どのサービスか自信がないときは、systemctl status で見るだけにしましょう。
User= を書かないと、サービスは root として動きます。スクリプトに不具合があったとき、システム全体を壊すおそれがあります。自分で作るサービスには、User= に作業用のユーザーを書いておきましょう。
Lv3 で作った Python のスクリプト(venv と .env を使うもの)をサービスにするときは、[Service] の書き方に3つ気をつけます。
- venv の Python を絶対パスで書く:サービスでは
source .venv/bin/activateが使えません。ExecStart=/home/kaito/プロジェクト/.venv/bin/python /home/kaito/プロジェクト/main.pyのように、venv の中のpythonを直接指定します(venv の記事の「activate しなくても.venv/bin/pythonで動く」と同じ考え方です)。 - 作業フォルダを決める:
WorkingDirectory=/home/kaito/プロジェクトを書くと、スクリプトの中のopen("data.csv")のような短いパスが、そのフォルダから数えられます。cron の記事でcdしてから動かしたのと同じ理由です。 - API キーは
.serviceに直接書かない:EnvironmentFile=/home/kaito/プロジェクト/.envと書くと、.env(=APIキーなどの秘密の設定を書いておくファイル)の名前=値を環境変数として読みこめます。APIキーは、Web のサービスを使うときに本人の代わりになる、合言葉のような文字列です。.serviceファイルはだれでも読める場所にあるので、キーをEnvironment=に直接書くより安全です(.envはchmod 600にしておきます)。
もう1つ、Python は、画面ではなくログに出すとき、print の内容をためてから少しずつ出すことがあります。journalctl にすぐ表示されないときは、ExecStart の python のあとに -u をつける(.venv/bin/python -u main.py)か、Environment=PYTHONUNBUFFERED=1 を足すと、すぐに出るようになります。
🤖 詰まったらAIに聞こう:サービスが起動しない
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Linuxの初心者です。systemdで自分のスクリプトをサービスにしましたが、起動しません。
原因の候補と、確かめる順番を教えてください。
【.serviceファイルの中身】ここに貼る(パスワードやトークンは消す)
【systemctl status 名前 --no-pager の結果】ここに貼る
【journalctl -u 名前 -n 30 --no-pager の結果】ここに貼る
【手で直接スクリプトを実行したときの結果】例:動く / エラーになる.service ファイルの Environment= に、API キーやパスワードを書いていることがあります。貼る前に「***」に置き換えてください。ログに、メールアドレスや IP アドレスが出ている場合も伏せましょう。
後片づけ
自動起動をやめて止め、.service ファイルを消してから、systemd に読み直してもらいます。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
sudo systemctl disable --now kaito-hello
sudo rm /etc/systemd/system/kaito-hello.service
sudo systemctl daemon-reload
systemctl status kaito-hello --no-pager
cd ~/linux-practice
rm -r systemd-test
ls(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl disable --now kaito-hello
Removed '/etc/systemd/system/multi-user.target.wants/kaito-hello.service'.
kaito@demo:~/linux-practice/systemd-test$ sudo rm /etc/systemd/system/kaito-hello.service
kaito@demo:~/linux-practice/systemd-test$ sudo systemctl daemon-reload
kaito@demo:~/linux-practice/systemd-test$ systemctl status kaito-hello --no-pager
Unit kaito-hello.service could not be found.
kaito@demo:~/linux-practice/systemd-test$ cd ~/linux-practice
kaito@demo:~/linux-practice$ rm -r systemd-test
kaito@demo:~/linux-practice$ ls
archive-test chmod-test edit-test find-test grep-test link-test perm-test redirect-testよくある質問
Q. status に「status=203/EXEC」と出て、起動しません。
systemd が ExecStart のコマンドを実行できなかった、という意味です。よくある原因は、パスのまちがい(絶対パスになっていない、つづりがちがう)、スクリプトに実行権限(x)がない、1行目の #!/bin/bash がない、の3つです。ls -l でパスと権限を確かめ、手で直接スクリプトを動かしてみましょう。
Q. .service ファイルを直したのに、前の内容のまま動きます。
systemd は、.service ファイルを直しただけでは読み直しません。直したあとは sudo systemctl daemon-reload をしてから、sudo systemctl restart 名前 で起動し直します。読み直しを忘れると、systemctl status に「changed on disk」のような、読み直しをすすめる表示が出ることがあります。
Q. WSL で systemctl を打つと、エラーになります。
WSL では、systemd が動いていない設定になっている場合があります(WSL のバージョンや設定など、環境により異なります)。Microsoft の説明では、WSL の中の /etc/wsl.conf に [boot] と systemd=true を書き、Windows 側で wsl --shutdown してから開き直すと、systemd が使えるようになります。また、WSL はウィンドウをすべて閉じると、しばらくして Linux ごと止まる場合があるため、「ずっと動かしておく」用途には VPS が向いています。
まとめ
- systemd は、Linux のサービスを起動・見守る係。
systemctlで操作する -
start・stopは「今」、enable・disableは「次の起動から」。両方はenable --now - 自分のサービスは
/etc/systemd/system/名前.serviceに書き、daemon-reloadで読み直す -
ExecStartは絶対パス、User=は作業用ユーザー、Restart=on-failureで止まっても起動し直す - 止めたり消したりするのは、自分で作ったサービスだけ
次の記事では、systemd のもう1つの便利な機能、systemd timer を学びます。cron とどうちがうのか、どちらを使えばいいのかを比べます。
次に読む記事
- 前の記事:VPSを借りたら最初にやること|ユーザー作成・SSHの強化・ufwのチェックリスト【初心者向け】
- 次の記事:systemd timerとcronの違い|Linuxの定期実行を使い分ける【初心者向け】
関連する記事
- Pythonで毎朝のCSV集計を自動化する(pandas+定期実行):サービスやタイマーで動かす Python のスクリプトを作りたい方に
- Pythonで毎日の作業を自動化する入門|ダウンロードフォルダを自動で整理しよう:常駐させたい自動化のアイデアを探している方に
- 自動化スタック総覧|GAS・n8n・Python・Notionの使い分け:サーバーで常駐させるべきか、ほかの方法で足りるかを考えたい方に