未経験〜駆け出しのエンジニア向けに、EC2 上でサービスを「常駐させる・OS再起動後も動かす・落ちても復活させる」を systemd で実現する方法をユースケース中心にまとめました。unitファイルの断片をそのまま使えます。

01なぜ systemd を学ぶのか

EC2 で Linux サーバーを運用すると、必ず「このプロセスを常駐させたい」「落ちたら自動で立ち上げ直したい」「OS再起動後も勝手に動いていてほしい」という場面に出会います。それを担うのが systemd です。

systemd は現在の主要ディストリビューションで標準の init システム(PID 1、最初に立ち上がる管理プロセス)兼サービス管理の仕組みです。Amazon Linux 2023 も systemd を採用しています(Amazon Linux では AL2 から upstart に代わって systemd が使われ、AL2023 でも継続しています)。RHEL系・Ubuntu など他の主要ディストロも同様なので、一度覚えれば環境をまたいで通用します。

現場のコツ:「なんとなく nohup や & でバックグラウンド起動」から卒業するのが第一歩です。unitファイル1枚に書いておけば、再起動しても・別のメンバーが引き継いでも、同じ挙動を再現できます。
本記事は Amazon Linux 2023 / EC2 を主な想定にしています。基礎から固めたい方は クラウドエンジニアの教科書(TCP/IP編) を、Linux コマンド全般は Linux 実践コマンドリファレンス を合わせてどうぞ。

02基本操作 — まずこの7つ

systemd の操作は systemctl に集約されています。サービス名(unit名)は末尾の .service を省略できます。設定変更を伴うコマンドは sudo が必要です。

# 起動・停止・再起動
sudo systemctl start  app.service     # 今すぐ起動
sudo systemctl stop   app.service     # 停止
sudo systemctl restart app.service    # 停止して起動(設定反映)
sudo systemctl reload app.service     # 無停止で設定再読込(対応サービスのみ)

# 自動起動(OS起動時に立ち上がるか)
sudo systemctl enable  app.service    # 自動起動をオン
sudo systemctl disable app.service    # 自動起動をオフ
sudo systemctl enable --now app.service   # 自動起動オン+今すぐ起動

# 状態確認
systemctl status app.service          # 稼働状態・直近ログ・PIDなど
systemctl is-active  app.service      # active / inactive をスクリプト向けに1語で
systemctl is-enabled app.service      # enabled / disabled

# unitファイルを編集したら忘れずに
sudo systemctl daemon-reload

status は人が読む用、is-active / is-enabled はシェルスクリプトやヘルスチェックで判定に使う用、と覚えると迷いません。

現場のコツ:enabledisable(自動起動の設定)と startstop(今の状態)は別物です。「enable したのに起動しない」のは正常で、今起動したいなら --now を付けるか start を別途叩きます。

03ログを読む — journalctl

systemd 管理のサービスは、標準出力・標準エラーがすべて journal(journald が管理するログ基盤)に集約されます。journalctl で横断的に読めるのが強みです。

journalctl -u app.service                 # このサービスのログ
journalctl -u app.service -f              # 追尾(tail -f 相当)
journalctl -u app.service --since "10 min ago"   # 直近10分
journalctl -u app.service --since today   # 今日ぶん
journalctl -u app.service -p err          # エラー以上(優先度でフィルタ)
journalctl -u app.service -b              # 今回の起動以降(前回は -b -1)
journalctl -u app.service -n 100 --no-pager   # 末尾100行だけ

トラブル時は「-u で対象を絞り、--since で時間を絞り、-p err で深刻なものだけ拾う」の3点セットが速いです。起動に失敗したときは -b(今回の起動)でブート直後のログを見ると原因が掴めます。

journal はディスクを消費します。放置すると肥大するので、AL2023 など多くの環境では /etc/systemd/journald.confSystemMaxUse= で上限を決めておくと安心です。使用量は journalctl --disk-usage、手動整理は sudo journalctl --vacuum-time=7d(7日より古いものを削除)で行えます。

ログをファイルに残したい・CloudWatch Logs に集約して監視やアラートに使いたい場合は、journal だけに頼らず CloudWatch Agent のログ収集設定 を併用します。

04ユースケース:自作アプリをサービス化する

一番よく使う場面です。Python / Node / Go などで書いた常駐プロセスを、unitファイル1枚で systemd 管理下に置きます。ここでは Gunicorn で動く Web アプリを例にします。

# /etc/systemd/system/app.service
[Unit]
Description=My App (Gunicorn)
After=network-online.target
Wants=network-online.target

[Service]
User=appuser
Group=appuser
WorkingDirectory=/opt/app
EnvironmentFile=/etc/app/app.env
ExecStart=/opt/app/venv/bin/gunicorn -b 127.0.0.1:8000 app:app
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

各セクションの意味を押さえておきましょう。

配置したら反映して有効化します。

sudo systemctl daemon-reload
sudo systemctl enable --now app.service
systemctl status app.service

環境変数は unitファイルに直書きせず、EnvironmentFile で別ファイル(例 /etc/app/app.env)に切り出すのがおすすめです。KEY=VALUE を1行ずつ書きます。

# /etc/app/app.env (このファイルのパーミッションは絞る)
APP_ENV=production
DB_HOST=db.internal.example
API_TOKEN=YOUR_TOKEN_HERE
現場のコツ:DBパスワードやAPIトークンなどの機密は、unitファイルにも Git にも直接書かないでください。EnvironmentFile のパーミッションを chmod 600 で絞る、あるいは SSM Parameter Store / Secrets Manager から起動時に取得する設計が安全です。

05ユースケース:EC2 起動時に自動で動かす

サービスを enable しておけば OS再起動後も自動起動します。加えて、EC2 インスタンスを新規に立ち上げる段階で unit を仕込みたい場合は、user-data(cloud-init)を使います。Auto Scaling で増えるインスタンスにも同じ設定が行き渡ります。

#!/bin/bash
# EC2 の user-data 例(初回起動時に一度だけ実行される)
dnf install -y python3-pip
# ... アプリの配置・依存インストールなど ...

# unitファイルを書き出して有効化
cat >/etc/systemd/system/app.service <<'UNIT'
[Unit]
Description=My App
After=network-online.target
Wants=network-online.target
[Service]
User=appuser
WorkingDirectory=/opt/app
ExecStart=/opt/app/venv/bin/gunicorn -b 127.0.0.1:8000 app:app
Restart=always
[Install]
WantedBy=multi-user.target
UNIT

systemctl daemon-reload
systemctl enable --now app.service
CloudWatch Agent 自体も systemd 管理のサービス(amazon-cloudwatch-agent.service)として動きます。エージェント設定を入れたあと sudo systemctl enable --now amazon-cloudwatch-agent のように起動・自動起動する、というのは本記事と同じ操作です。ログ/メトリクス収集の具体的な設定は CloudWatch Agent のログ設定ガイド を参照してください。

cloud-init のログ(user-data がうまく動いたか)は /var/log/cloud-init-output.log に出ます。うまく起動しないときはまずここを確認します。

06ユースケース:落ちても自動で復活させる

常駐プロセスは、バグやメモリ不足、外部依存の一時障害などで落ちることがあります。systemd の Restart= で自動復旧を宣言できます。

[Service]
ExecStart=/opt/app/venv/bin/gunicorn -b 127.0.0.1:8000 app:app
Restart=on-failure     # 異常終了(0以外)のときだけ再起動
# Restart=always       # 正常終了でも常に再起動(常駐サービス向け)
RestartSec=5           # 再起動まで5秒待つ(連続クラッシュ時の暴走を防ぐ)
StartLimitIntervalSec=60
StartLimitBurst=5      # 60秒に5回を超えて再起動したら諦めて failed にする

起動の順序・依存関係も宣言できます。DB や別サービスが立ち上がってから起動したいときに使います。

[Unit]
After=network-online.target postgresql.service   # これらの後に起動を試みる
Wants=network-online.target                        # 弱い依存(無くても自分は起動する)
Requires=postgresql.service                        # 強い依存(相手が失敗すると自分も止まる)
現場のコツ:After は「順序」だけ、Requires / Wants は「依存」を表します。順序と依存は別概念で、両方を書いてはじめて「DBが上がった後に起動」が成立します。まずは AfterWants の緩い組み合わせから始めると事故りにくいです。

07ユースケース:定期実行(systemd timer と cron)

日次バックアップやログローテーションなど「定期的に1回だけ走らせたい」処理は、systemd timer か cron で行います。timer は「実行する処理(.service)」と「いつ実行するか(.timer)」の2ファイルに分けて書きます。

# /etc/systemd/system/app-backup.service
[Unit]
Description=Nightly backup to S3

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/app-backup.timer
[Unit]
Description=Run backup every night at 02:30

[Timer]
OnCalendar=*-*-* 02:30:00     # 毎日 02:30。曜日や月も指定可
Persistent=true              # 停止中に来た分を次回起動時に取り返す

[Install]
WantedBy=timers.target

有効化と確認は次の通りです。timer は .timer の方を enable します。

sudo systemctl daemon-reload
sudo systemctl enable --now app-backup.timer
systemctl list-timers          # 次回実行時刻の一覧
journalctl -u app-backup.service   # 実行結果のログ

cron との使い分けの目安です。

観点systemd timercron
ログjournalctl -u で他サービスと統一既定では別(メール/自作ログ)
依存関係After= 等で宣言できる書けない
停止中の取りこぼしPersistent=true で取り返せる基本は取りこぼす
手軽さunit 2枚が必要でやや冗長1行で書けて手軽
サーバーを常駐させ続けずに定期処理だけしたいなら、EC2+timer/cron より EventBridge Scheduler + Lambda の方が管理対象を減らせます。「そもそもこの1台を動かし続ける必要があるか?」を先に考えると、サーバーレスとの切り分けがきれいになります。

08つまづきポイント

現場のコツ:起動に失敗したら、まず systemctl status app.service で末尾のエラー行を見て、続けて journalctl -u app.service -b で今回の起動ログを追う。この2手でほとんどの原因(パス・権限・環境変数)は切り分けられます。

09早見表 — 逆引きチートシート

「やりたいこと」から引ける systemctl / journalctl の逆引きです。1画面で確認できるよう主要なものだけ載せます。

やりたいことコマンド
今すぐ起動 / 停止systemctl start / stop app
設定を反映して再起動systemctl restart app
自動起動オン+今すぐsystemctl enable --now app
自動起動オフsystemctl disable app
状態を見るsystemctl status app
稼働中か1語で判定systemctl is-active app
unit編集を反映systemctl daemon-reload
ログを追尾journalctl -u app -f
直近10分のログjournalctl -u app --since "10 min ago"
エラーだけjournalctl -u app -p err
今回の起動以降journalctl -u app -b
タイマー一覧・次回実行systemctl list-timers
失敗した unit 一覧systemctl --failed

unitファイルの主要ディレクティブも早見でまとめます。

セクションよく使うキー役割
[Unit]Description / After / Wants / Requires説明・起動順序・依存関係
[Service]ExecStart / User / WorkingDirectory / EnvironmentFile起動コマンドと実行環境(パスは絶対)
[Service]Restart / RestartSec / Type自動復旧の挙動と起動タイプ
[Install]WantedBy=multi-user.targetenable 時の紐付け先(常駐サービスの定番)
[Timer]OnCalendar / Persistent実行スケジュールと取りこぼし対策

関連記事:Linux 実践コマンドリファレンスCloudWatch Agent ログ設定ガイドPython × Lambda 実践ユースケース。基礎は クラウドエンジニアの教科書 で。

EMW は札幌を拠点に、EC2 を含む AWS 運用の自動化・監視を伴走しています。基礎を積みたい方の学びも歓迎です。採用情報・カジュアル面談はこちら

10よくある質問(FAQ)

systemctl enablestart の違いは?

start は「今すぐ起動する」だけで、再起動すると止まったままです。enable は「次回以降の起動時に自動で立ち上げる」設定で、今は起動しません。両方を一度に済ませたいときは systemctl enable --now app.service を使います。EC2 では再起動やAuto Scalingでの入れ替えが起きるので、常駐させたいサービスは必ず enable しておきます。

unitファイルを直接 vi で書き換えたのに反映されません。

unitファイル(.service / .timer)を編集したら sudo systemctl daemon-reload が必須です。これを忘れると systemd は古い定義のまま動きます。設定を変えたら「daemon-reload → restart」の2手をセットで覚えてください。

ログはどこを見ればいいですか? /var/log に出ません。

systemd 管理のサービスは標準出力・標準エラーが journal に集約されます。journalctl -u app.service で確認し、追いかけたいときは -f、直近だけなら --since "10 min ago" を付けます。ファイルとして残したい・CloudWatch に送りたい場合は CloudWatch Agent を併用します。

定期実行は cron と systemd timer のどちらが良いですか?

1台のサーバー内で完結する定期処理なら、ログが journal に統一され依存関係も書ける systemd timer が扱いやすいです。ただしスケジュール実行そのものをサーバーレスにしたい・複数リソースをまたぐなら EventBridge + Lambda が適します。「サーバーを常駐させ続ける必要があるか」で選ぶのがコツです。

EMW では EC2 を含む AWS 運用の自動化・監視・障害対応を、基礎から一緒に積み上げていける仲間を探しています。systemd のような地味だが効く土台づくりに興味がある方は、お気軽にご相談ください。

相談する
← ブログ一覧へ戻る