systemd timerとcronの違い|Linuxの定期実行を使い分ける【初心者向け】

前の記事では、systemd で自分のスクリプトをサービスにして、自動起動させました。今回は、systemd のもう1つの機能、systemd timer(タイマー)を使って「決まった時間に動かす」を試します。

私は毎朝のレポート作りを cron で動かしていましたが、ある日レポートが届いていませんでした。調べようとしても、cron は「動いたのか」「失敗したのか」がわかりにくく、ログを探すだけで時間がかかりました。systemd timer に変えてからは、systemctl list-timers で「次はいつ動くか」、journalctl で「前回どうだったか」がすぐ見られるようになりました。

この記事では、1分ごとに動く練習用のタイマーを作り、cron とのちがいと使い分けを整理します。

💡
この記事でわかること
  • systemd timer と cron のちがい
  • タイマーが「.service」と「.timer」の2つのファイルでできている理由
  • OnCalendar= の書き方と、systemd-analyze calendar での確かめ方
  • systemctl list-timers で、次に動く時刻を見る方法
  • 電源が切れていた間の分を取り戻す Persistent=true
🔰
この記事の前提

まだの方は、前の記事「Linuxのsystemd入門|systemctlでサービスを起動・自動起動する【初心者向け】」から読むのがおすすめです。

💡
用語メモ
  • Webhook:何かが起きたときに、決めた URL へ自動で知らせを送るしくみ
  • サービス:systemd が管理する、裏でずっと動いて仕事を受け付けるプログラム(デーモン)。例:ssh.service
  • cron:決まった日時に、コマンドをくり返し自動で実行してくれるしくみ。裏で動く cron デーモンが、crontab に書いた予定を見て動かします
  • journalctl:systemd が集めたログを見るコマンド

用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。


結論:かんたんなら cron、見える化したいなら timer

くらべるところ cron systemd timer
設定の手軽さ 1行で書ける ファイルが2つ必要
次に動く時刻の確認 自分で計算する systemctl list-timers で見られる
実行結果のログ 自分でファイルに書き出す journalctl に自動で残る
電源が切れていた間の分 実行されない Persistent=true で、起動後に1回動く
手で1回だけ動かす コマンドを自分で打つ systemctl start 名前.service
向いている場面 自分のパソコン・かんたんな定期実行 サーバー・失敗に気づきたい定期実行

systemd timer のしくみ

準備:練習用のスクリプトを作る

  • 1
  • 2
  • 3
mkdir -p ~/linux-practice/timer-test
cd ~/linux-practice/timer-test
nano report.sh

次の内容を書いて保存します。実行するたびに、時刻を report.log に1行足すスクリプトです。

  • 1
  • 2
  • 3
#!/bin/bash
echo "レポートを作りました: $(date '+%Y-%m-%d %H:%M:%S')" >> /home/kaito/linux-practice/timer-test/report.log
echo "kaito-report: 実行しました"

  • 1
  • 2
  • 3
chmod +x report.sh
./report.sh
cat report.log

🖥️
この記事の出力例について

ここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
kaito@demo:~/linux-practice$ mkdir -p ~/linux-practice/timer-test
kaito@demo:~/linux-practice$ cd ~/linux-practice/timer-test
kaito@demo:~/linux-practice/timer-test$ nano report.sh
kaito@demo:~/linux-practice/timer-test$ chmod +x report.sh
kaito@demo:~/linux-practice/timer-test$ ./report.sh
kaito-report: 実行しました
kaito@demo:~/linux-practice/timer-test$ cat report.log
レポートを作りました: 2026-11-01 03:44:21

画面に出るのは2行目の echo だけで、1行目の内容は report.log に書き込まれています。

まず手で動かして、スクリプトが正しく動くことを確かめておくのが大切です。


STEP 1:.service ファイルを作る(何をするか)

タイマーは「いつ」、サービスは「何を」を受け持ちます。まず「何を」から書きます。

  • 1
sudo nano /etc/systemd/system/kaito-report.service

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
[Unit]
Description=kaito の練習用レポート

[Service]
Type=oneshot
User=kaito
ExecStart=/home/kaito/linux-practice/timer-test/report.sh

Type=oneshot は「1回動いて終わる」という意味です。前の記事のサービスのように、ずっと動き続けるものではありません。[Install] は書きません(自動起動するのはタイマーのほうだからです)。

  • 1
  • 2
  • 3
sudo systemctl daemon-reload
sudo systemctl start kaito-report.service
cat ~/linux-practice/timer-test/report.log

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
kaito@demo:~/linux-practice/timer-test$ sudo systemctl daemon-reload
kaito@demo:~/linux-practice/timer-test$ sudo systemctl start kaito-report.service
kaito@demo:~/linux-practice/timer-test$ cat ~/linux-practice/timer-test/report.log
レポートを作りました: 2026-11-01 03:44:21
レポートを作りました: 2026-11-01 03:44:34

systemctl start で動かした分の1行が増えて、2行になりました。

手で1回だけ動かせるのが、systemd timer の便利なところです。「cron で動かすとうまくいかない」を調べるときにも役立ちます。


STEP 2:.timer ファイルを作る(いつ動かすか)

サービスと同じ名前で、拡張子(=ファイル名の最後の .txt や .py の部分)だけ .timer にしたファイルを作ります。同じ名前どうしが自動で結びつきます。

  • 1
sudo nano /etc/systemd/system/kaito-report.timer

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
[Unit]
Description=kaito の練習用レポートを毎分動かす

[Timer]
OnCalendar=*-*-* *:*:00
AccuracySec=1s
Persistent=true

[Install]
WantedBy=timers.target

書く場所 意味
OnCalendar=*-*-* *:*:00 毎日・毎時・毎分の0秒に動かす
AccuracySec=1s 時刻のずれを1秒以内にする(ふだんは書かなくてよい)
Persistent=true 電源が切れていて動けなかった分を、起動後に1回動かす
WantedBy=timers.target enable したとき、タイマーの仲間に加える

STEP 3:OnCalendar の書き方を確かめる

systemd-analyze calendar を使うと、書いた時刻が「次はいつになるか」を、タイマーを動かす前に確かめられます。

  • 1
  • 2
  • 3
systemd-analyze calendar "*-*-* *:*:00"
systemd-analyze calendar "*-*-* 07:00:00"
systemd-analyze calendar "Mon..Fri 09:30"

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
kaito@demo:~/linux-practice/timer-test$ systemd-analyze calendar "*-*-* *:*:00"
Normalized form: *-*-* *:*:00
    Next elapse: Sun 2026-11-01 03:45:00 JST
       (in UTC): Sat 2026-10-31 18:45:00 UTC
       From now: 13s left
kaito@demo:~/linux-practice/timer-test$ systemd-analyze calendar "*-*-* 07:00:00"
Normalized form: *-*-* 07:00:00
    Next elapse: Sun 2026-11-01 07:00:00 JST
       (in UTC): Sat 2026-10-31 22:00:00 UTC
       From now: 3h 15min left
kaito@demo:~/linux-practice/timer-test$ systemd-analyze calendar "Mon..Fri 09:30"
  Original form: Mon..Fri 09:30
Normalized form: Mon..Fri *-*-* 09:30:00
    Next elapse: Mon 2026-11-02 09:30:00 JST
       (in UTC): Mon 2026-11-02 00:30:00 UTC
       From now: 1 day 5h left

Next elapse が次に動く日時、(in UTC) が同じ日時を世界標準時で書いたもの、From now がそれまでの残り時間です。Original form(書いたとおりの形)は、Mon..Fri 09:30 のように、書いた形と Normalized form(systemd が読み取った正式な形)がちがうときだけ表示されます。日時は、検証したとき(11月1日の3時44分ごろ)から見た値です。

Debian 13での実行画面

やりたいこと OnCalendar の書き方 cron で書くと
毎朝7時 *-*-* 07:00:00 0 7 * * *
平日の9時30分 Mon..Fri 09:30 30 9 * * 1-5
1時間ごと hourly 0 * * * *
毎月1日の0時 monthly 0 0 1 * *

💡
本物のタイマーで知っておきたい2つの設定(文章だけで説明します)
  • Persistent=true が動く場面:「電源が切れていた」だけでなく、パソコンを止めていて(タイマーが動いていなくて)予定の時刻を過ぎた場合に、次に起動したとき1回だけ動きます。何回分を飛ばしても、動くのは1回です。ノートパソコンのスリープ中や、WSL を閉じていたあいだの扱いは、環境により異なります。毎朝7時の作業が「朝はパソコンを閉じていた日」も動いてほしいときに便利です。
  • RandomizedDelaySec=:RandomizedDelaySec=10min のように書くと、決めた時刻から最大10分のあいだで、毎回ランダムに少し遅らせて動きます。外の API(=プログラム同士がデータをやりとりするための窓口)やサイトに取りにいく作業を、ちょうど0分に集中させないための設定です(Lv3 の curl の記事で、相手のサーバーに負担をかけない話をしました)。

書いたあとは、STEP 4 の systemctl list-timers で、次に動く時刻を確かめましょう。


STEP 4:タイマーを動かす

タイマーを読み込んで、enable --now で「今から動かす&次の起動からも動かす」にします。

  • 1
  • 2
  • 3
sudo systemctl daemon-reload
sudo systemctl enable --now kaito-report.timer
systemctl list-timers kaito-report.timer --no-pager

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
kaito@demo:~/linux-practice/timer-test$ sudo systemctl daemon-reload
kaito@demo:~/linux-practice/timer-test$ sudo systemctl enable --now kaito-report.timer
Created symlink '/etc/systemd/system/timers.target.wants/kaito-report.timer' → '/etc/systemd/system/kaito-report.timer'.
kaito@demo:~/linux-practice/timer-test$ systemctl list-timers kaito-report.timer --no-pager
NEXT                        LEFT LAST PASSED UNIT               ACTIVATES
Sun 2026-11-01 03:45:00 JST   5s -         - kaito-report.timer kaito-report.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.

検証したときはまだ1回も動いていなかったので、LAST(前回)と PASSED(前回からの時間)は - になっています。最後の Pass --all … は、「止まっているタイマーも見たいときは --all をつけてね」という意味です。

NEXT が次に動く時刻、LEFT がそれまでの残り時間です。2〜3分待ってから、結果を確かめましょう。

  • 1
  • 2
cat ~/linux-practice/timer-test/report.log
journalctl -u kaito-report.service -n 10 --no-pager

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
kaito@demo:~/linux-practice/timer-test$ cat ~/linux-practice/timer-test/report.log
レポートを作りました: 2026-11-01 03:44:21
レポートを作りました: 2026-11-01 03:44:34
レポートを作りました: 2026-11-01 03:45:00
レポートを作りました: 2026-11-01 03:46:00
レポートを作りました: 2026-11-01 03:47:00
kaito@demo:~/linux-practice/timer-test$ journalctl -u kaito-report.service -n 10 --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:44:34 demo report.sh[2039]: kaito-report: 実行しました
Nov 01 03:45:00 demo report.sh[2108]: kaito-report: 実行しました
Nov 01 03:46:00 demo report.sh[2115]: kaito-report: 実行しました
Nov 01 03:47:00 demo report.sh[2118]: kaito-report: 実行しました

report.log には、手で動かした2行のあとに、03:45:00・03:46:00・03:47:00 と毎分0秒ちょうどに行が増えています。

journalctl の最初の3行の Hint は、前の記事で説明した「ほかのユーザーやシステムのログは見えていません」という注意です。そのため、一般ユーザーの kaito には、スクリプトが表示した kaito-report: 実行しました の行だけが見えています。systemd 自身が書く Starting kaito-report.service… や Finished kaito-report.service … の行は、一般ユーザーには表示されません(Hint にあるとおり、adm や systemd-journal グループのユーザーなら見られます)。

Debian 13での実行画面

💡
失敗したときに気づく方法

cron の記事では、cron が失敗しても何も教えてくれないので、ログを自分でファイルに書き出しました。systemd timer では、スクリプトがエラーの終了コード(0 以外)で終わると、サービスが failed(失敗)の状態になって残ります。

  • systemctl status kaito-report.service --no-pager で、前回の結果(Active: の行)と、最後のログの数行を見られます。
  • systemctl --failed で、失敗したままのサービスの一覧を見られます。何も並ばなければ、失敗はありません。
  • systemctl list-timers --all で、止まっているタイマーも含めて、前回(LAST)と次回(NEXT)の時刻を見られます。「タイマーが動いていない」と思ったら、まずここを見ます。

慣れてきたら、.service の [Unit] に OnFailure= を書いて、失敗したときに別のサービス(通知を送るものなど)を動かすこともできます。

🚫
cron と timer で、同じ作業を二重に動かさない

cron から systemd timer に乗りかえるときは、crontab -e で古い行を消すか、行の先頭に # をつけて止めてから、タイマーを有効にしてください。両方残っていると、同じメールが2通届いたり、同じファイルに二重に書き込んだりします。今の cron の設定は crontab -l で確かめられます。

🚫
毎分の実行で重い処理や外部への送信をしない

この記事の「毎分」は、すぐに結果を見るための練習用です。本物のタイマーで、メールの送信や外部の API の呼び出し、大きなファイルのコピーを毎分動かすと、相手のサービスに迷惑をかけたり、ディスク(=データをしまっておく部品)がいっぱいになったりします。練習が終わったら、必ず後片づけで止めましょう。

🤖 詰まったらAIに聞こう:cron の設定を timer に書きかえたい

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
Linuxの初心者です。cronで動かしている定期実行を、systemd timerに書きかえたいです。
.serviceと.timerの2つのファイルの中身と、有効にする手順、
動いたかを確かめる方法を教えてください。

【今のcrontabの行】例:0 7 * * * /home/kaito/scripts/report.sh
【動かすユーザー】例:kaito
【やりたいこと】例:毎朝7時に動かしたい。PCが止まっていた日は起動後に1回動かしたい
【使っているOS】例:Debian 13 / Ubuntu 24.04

🔒
AIに貼る前に:秘密情報やパスワードは貼らない

crontab やスクリプトに、API キー・パスワード・Webhook の URLを直接書いていることがあります。貼る前に「***」に置き換えてください。ユーザー名やパスも、気になる場合は「kaito」などに置き換えましょう。


後片づけ

タイマーを止めて、2つのファイルを消し、読み直します。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
sudo systemctl disable --now kaito-report.timer
sudo rm /etc/systemd/system/kaito-report.timer /etc/systemd/system/kaito-report.service
sudo systemctl daemon-reload
systemctl list-timers --all --no-pager | grep kaito
cd ~/linux-practice
rm -r timer-test
ls

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
kaito@demo:~/linux-practice/timer-test$ sudo systemctl disable --now kaito-report.timer
Removed '/etc/systemd/system/timers.target.wants/kaito-report.timer'.
kaito@demo:~/linux-practice/timer-test$ sudo rm /etc/systemd/system/kaito-report.timer /etc/systemd/system/kaito-report.service
kaito@demo:~/linux-practice/timer-test$ sudo systemctl daemon-reload
kaito@demo:~/linux-practice/timer-test$ systemctl list-timers --all --no-pager | grep kaito
kaito@demo:~/linux-practice/timer-test$ cd ~/linux-practice
kaito@demo:~/linux-practice$ rm -r timer-test
kaito@demo:~/linux-practice$ ls
archive-test  chmod-test  edit-test  find-test  grep-test  link-test  perm-test  redirect-test

よくある質問

Q. タイマーを作ったのに、list-timers に出てきません。

enable --now を忘れているか、.timer を作ったあとの daemon-reload を忘れていることが多いです。systemctl list-timers --all で止まっているタイマーも表示し、systemctl status kaito-report.timer で Loaded: と Active: を確かめましょう。.timer と .service の名前(拡張子の前)がちがうと、結びつきません。

Q. cron とタイマーのどちらを使えばいいか、迷います。

自分のパソコンで「毎朝なにかを動かす」くらいなら、1行で書ける cron(Lv3 の cron 入門の記事)で十分です。サーバーで動かす作業や、失敗に気づきたい作業、止まっていた日の分も動かしたい作業は、タイマーが向いています。どちらにしても、同じ作業を両方に書かないようにしましょう。

Q. タイマーで動かすスクリプトに、API キーを使いたいです。

前の記事(systemd 入門)の Python の箱と同じく、.service の [Service] に EnvironmentFile= で .env(=APIキーなどの秘密の設定を書いておくファイル)を読みこむ形にします。API キーを .service や .timer に直接書かないでください。


まとめ

  • systemd timer は「.timer(いつ)」と「.service(何を)」の2つのファイルで動く。名前をそろえる
  • 1回で終わる作業は Type=oneshot。手で1回だけ動かすなら systemctl start 名前.service
  • 時刻は OnCalendar= で書き、systemd-analyze calendar で事前に確かめる
  • systemctl list-timers で次の実行時刻、journalctl -u で結果が見られる
  • Persistent=true で、電源が切れていた間の分を起動後に取り戻せる
  • かんたんなら cron、サーバーで失敗に気づきたいなら timer。二重に動かさない

次の記事では、今回も使った journalctl を中心に、Linux のログの見方を学びます。「動かない」「止まった」ときに、原因を探す方法です。


次に読む記事


関連する記事