Cheatsheet and Quiz
Use this page as a quick reference after you've completed the full documentation. The cheat sheet is designed for copy-paste during daily operations. The quiz tests your understanding of key concepts.
Cheat Sheet
Complete systemd timer Cheat Sheet — Click to Expand
# ══════════════════════════════════════════════════════════════════════════════
# systemd timer — Quick Reference Cheat Sheet
# ══════════════════════════════════════════════════════════════════════════════
# ── STEP 1: Create the Timer Unit ─────────────────────────────────────────────
# File: /etc/systemd/system/myjob.timer
[Unit]
Description=Run my job on a schedule
[Timer]
OnCalendar=02:15 # Calendar-based (see patterns below)
Persistent=true # Catch up missed runs after downtime
RandomizedDelaySec=5m # Fleet jitter: 0–5 min random delay
FixedRandomDelay=true # Stable offset per unit across runs
AccuracySec=1m # Coalescing window (lower = more precise)
# Unit=another.service # Override default name match
[Install]
WantedBy=timers.target
# ── STEP 2: Create the Service Unit ──────────────────────────────────────────
# File: /etc/systemd/system/myjob.service
[Unit]
Description=My scheduled job
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=www-data
Group=www-data
WorkingDirectory=/var/www/html
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/usr/bin/flock -n /var/lock/myjob.lock /usr/local/bin/myjob.sh
RuntimeMaxSec=1h
StandardOutput=append:/var/log/myjob.log
StandardError=append:/var/log/myjob.log
NoNewPrivileges=true # Basic hardening
PrivateTmp=true
# No [Install] section — activated by the .timer unit
# ── STEP 3: Activate ─────────────────────────────────────────────────────────
sudo systemctl daemon-reload
sudo systemctl enable --now myjob.timer
# ── Management Commands ──────────────────────────────────────────────────────
sudo systemctl daemon-reload # Re-read unit files (REQUIRED)
sudo systemctl enable --now myjob.timer # Enable on boot + start now
sudo systemctl disable --now myjob.timer # Disable + stop
sudo systemctl start myjob.timer # Start the timer
sudo systemctl stop myjob.timer # Stop the timer
sudo systemctl restart myjob.timer # Restart the timer
systemctl status myjob.timer # Check schedule / next fire
systemctl status myjob.service # Check service result / exit code
# ── Inspect ──────────────────────────────────────────────────────────────────
systemctl list-timers --all --no-pager # All timers with NEXT/LAST/PASSED
systemctl list-timers myjob.timer # Specific timer
systemctl show myjob.timer -p NextElapseUSecRealtime -p LastTriggerUSec
systemctl cat myjob.timer # Show unit file content
# ── Logs ─────────────────────────────────────────────────────────────────────
journalctl -u myjob.service -f # Follow live output
journalctl -u myjob.service -n 50 # Last 50 lines
journalctl -u myjob.service --since today # Today's logs
journalctl -u myjob.service -p err # Errors only
journalctl -u myjob.timer -u myjob.service # Combined view
# ── Test ─────────────────────────────────────────────────────────────────────
sudo systemctl start myjob.service # Run service immediately (no wait)
journalctl -u myjob.service -n 20 # Check result
# ── Schedule Validation ──────────────────────────────────────────────────────
systemd-analyze calendar --iterations=5 'Mon..Fri 06:00'
systemd-analyze calendar --iterations=3 '*:0/5'
systemd-analyze calendar --iterations=3 '*-*~1 00:00:00'
# ── Verify + Security ────────────────────────────────────────────────────────
sudo systemd-analyze verify myjob.timer myjob.service
systemd-analyze security myjob.service
# ── Persistent State ─────────────────────────────────────────────────────────
sudo systemctl clean --what=state myjob.timer
# ── User Timers (no root) ────────────────────────────────────────────────────
# Files go in: ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now myjob.timer
systemctl --user list-timers --no-pager
sudo loginctl enable-linger "$USER" # Persist after logout
# ── Full Cleanup ─────────────────────────────────────────────────────────────
sudo systemctl disable --now myjob.timer
sudo rm /etc/systemd/system/myjob.timer /etc/systemd/system/myjob.service
sudo systemctl daemon-reload
sudo systemctl reset-failed
OnCalendar Quick Reference
| Pattern | Expression | Validate |
|---|---|---|
| Every minute | minutely | systemd-analyze calendar minutely |
| Every 5 minutes | *:0/5 | systemd-analyze calendar '*:0/5' |
| Every 15 minutes | *:0/15 | systemd-analyze calendar '*:0/15' |
| Every 30 minutes | *:0/30 | systemd-analyze calendar '*:0/30' |
| Every hour | hourly | systemd-analyze calendar hourly |
| Every 2 hours | *-*-* 0/2:00:00 | systemd-analyze calendar '*-*-* 0/2:00:00' |
| Every 6 hours | *-*-* 00/6:00:00 | systemd-analyze calendar '*-*-* 00/6:00:00' |
| Midnight daily | daily | systemd-analyze calendar daily |
| Daily at 2:15 AM | 02:15 | systemd-analyze calendar '02:15' |
| Twice daily | 09,21:00 | systemd-analyze calendar '09,21:00' |
| Peak hours (8-8, /10m) | *-*-* 08..20:0/10 | systemd-analyze calendar '*-*-* 08..20:0/10' |
| Weekdays at 6 AM | Mon..Fri 06:00 | systemd-analyze calendar 'Mon..Fri 06:00' |
| Monday at 3 AM | Mon 03:00 | systemd-analyze calendar 'Mon 03:00' |
| Saturday at 2 AM | Sat 02:00 | systemd-analyze calendar 'Sat 02:00' |
| 1st of month | monthly | systemd-analyze calendar monthly |
| 1st and 15th | *-*-01,15 04:00 | systemd-analyze calendar '*-*-01,15 04:00' |
| Last day of month | *-*~1 00:00:00 | systemd-analyze calendar '*-*~1 00:00:00' |
| First Monday | Mon *-*-1..7 03:00 | systemd-analyze calendar 'Mon *-*-1..7 03:00' |
| Quarterly | quarterly | systemd-analyze calendar quarterly |
| Yearly | yearly | systemd-analyze calendar yearly |
Monotonic Timer Reference
| Directive | Counts From | Example |
|---|---|---|
OnBootSec= | System boot | OnBootSec=2min |
OnStartupSec= | systemd manager start | OnStartupSec=30s |
OnUnitActiveSec= | Timer activation (fixed interval) | OnUnitActiveSec=1h |
OnUnitInactiveSec= | Service completion (cooldown) | OnUnitInactiveSec=20m |
Debugging Quick Reference
| Symptom | Likely Cause | Fix |
|---|---|---|
| Timer never fires | Bad calendar expression or daemon-reload not run | Validate + reload |
| Unit file error | Syntax error in unit file | systemd-analyze verify |
| Service fails immediately | Wrong path, permissions, or env | Check User=, absolute paths, journal |
| Missed runs after reboot | Persistent=true missing | Add it |
| Fleet thundering herd | No jitter | Add RandomizedDelaySec= |
| Job overlaps itself | No flock or runtime limit | Add flock -n + RuntimeMaxSec= |
| Timer state stale | Persistent state corrupted | systemctl clean --what=state |
| User timer stops on logout | Linger not enabled | loginctl enable-linger |
Hands-On Lab
Lab 1 — Basic Timer (5 minutes)
- Create
/etc/systemd/system/lab-timer.servicewithType=oneshotandExecStart=/bin/sh -c 'echo "Timer fired at $(date)" >> /tmp/lab-timer.log'. - Create
/etc/systemd/system/lab-timer.timerwithOnUnitActiveSec=30s. - Enable:
sudo systemctl daemon-reload && sudo systemctl enable --now lab-timer.timer. - Watch:
tail -f /tmp/lab-timer.log. - After 2 minutes, verify 3–4 entries in the log.
Lab 2 — Calendar Timer (5 minutes)
- Create
/etc/systemd/system/lab-calendar.timerwithOnCalendar=*:0/1(every minute). - Create a matching service that writes to
/tmp/lab-calendar.log. - Validate:
systemd-analyze calendar --iterations=3 '*:0/1'. - Enable and wait 3 minutes.
- Check:
wc -l /tmp/lab-calendar.logshould show ~3 lines.
Lab 3 — Persistent Catch-Up (5 minutes)
- Create a timer with
OnCalendar=*:0/2andPersistent=true. - Enable it and let it fire 2 times.
- Stop the timer:
sudo systemctl stop lab-persistent.timer. - Wait 5 minutes (let 2 runs be "missed").
- Start the timer again:
sudo systemctl start lab-persistent.timer. - Check logs — the service should fire immediately to catch up.
Lab 4 — Validate Expressions (5 minutes)
Practice validating these. Predict the output before running:
systemd-analyze calendar --iterations=3 '*-*~1 00:00:00'
systemd-analyze calendar --iterations=3 'Mon *-*-1..7 03:00:00'
systemd-analyze calendar --iterations=5 '*-*-* 08..20:0/10'
systemd-analyze calendar --iterations=4 '09,21:00'
Lab 5 — Cleanup
for unit in lab-timer lab-calendar lab-persistent; do
sudo systemctl disable --now "${unit}.timer" 2>/dev/null || true
sudo rm -f "/etc/systemd/system/${unit}.timer" "/etc/systemd/system/${unit}.service"
done
sudo systemctl daemon-reload
rm -f /tmp/lab-timer.log /tmp/lab-calendar.log /tmp/lab-persistent.log
Build Task — Complete WordPress Maintenance Timer Set
Create four timer/service pairs that run:
- DB backup daily at 2:30 AM with
flockandRuntimeMaxSec - Backup purge daily at 4:00 AM
- Disk usage alert every 10 minutes
- Object cache flush every 6 hours
All must use Persistent=true and have security hardening.
Solution
[Unit]
Description=WordPress DB backup daily at 02:30
[Timer]
OnCalendar=02:30
Persistent=true
RandomizedDelaySec=5m
[Install]
WantedBy=timers.target
[Unit]
Description=WordPress database export
[Service]
Type=oneshot
User=www-data
ExecStart=/usr/bin/flock -n /var/lock/wp-db.lock /usr/local/bin/wp db export /mnt/backups/wp-db.sql --path=/var/www/html
RuntimeMaxSec=1h
StandardOutput=append:/var/log/wp-db-backup.log
StandardError=append:/var/log/wp-db-backup.log
NoNewPrivileges=true
PrivateTmp=true
[Unit]
Description=Purge old backups daily at 04:00
[Timer]
OnCalendar=04:00
Persistent=true
[Install]
WantedBy=timers.target
[Unit]
Description=Delete backup files older than 14 days
[Service]
Type=oneshot
ExecStart=/usr/bin/find /mnt/backups -name "*.sql" -mtime +14 -delete
StandardOutput=append:/var/log/backup-prune.log
NoNewPrivileges=true
PrivateTmp=true
[Unit]
Description=Check disk usage every 10 minutes
[Timer]
OnCalendar=*:0/10
Persistent=true
[Install]
WantedBy=timers.target
[Unit]
Description=Disk usage alert
[Service]
Type=oneshot
ExecStart=/usr/local/bin/disk-alert.sh
StandardOutput=append:/var/log/disk-alert.log
NoNewPrivileges=true
PrivateTmp=true
[Unit]
Description=Flush WP object cache every 6 hours
[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true
[Install]
WantedBy=timers.target
[Unit]
Description=Flush WordPress object cache
[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/wp cache flush --path=/var/www/html
StandardOutput=append:/var/log/wp-cache-flush.log
NoNewPrivileges=true
PrivateTmp=true
sudo systemctl daemon-reload
sudo systemctl enable --now wp-db-backup.timer backup-prune.timer disk-alert.timer wp-cache-flush.timer
systemctl list-timers --no-pager | grep -E 'wp-|backup-|disk-'
Mini Quiz
Questions
-
What two unit files make up a complete systemd timer workflow?
-
What is the difference between
OnCalendar=andOnUnitActiveSec=? -
What does
Persistent=truedo, and which timer type does it affect? -
Why use
RandomizedDelaySec=for multi-server fleets? -
Which command validates the next 5 fire times for a calendar expression?
-
What command must you run after creating or editing any unit file?
-
How do you inspect logs from a timer-triggered service?
-
Why is
Type=oneshotthe recommended service type for timer jobs? -
What is the safe overlap prevention pattern for long-running jobs?
-
How do you make user timers continue running when the user is logged out?
Answers
-
A
.timerunit (defines when to run) and a.serviceunit (defines what to run). -
OnCalendar=fires at wall-clock times (like cron) — e.g., "every day at 2:15 AM."OnUnitActiveSec=fires at fixed intervals measured from the last timer activation — e.g., "every 60 seconds." -
Persistent=truestores the last trigger time on disk. After a reboot or downtime, any missed calendar runs are caught up at the next timer evaluation. It only affectsOnCalendar=timers — not monotonic timers. -
To prevent synchronized load spikes. Without jitter, 10 servers all scheduled at
02:15hit the backup target simultaneously.RandomizedDelaySec=5mspreads them across a 5-minute window. -
systemd-analyze calendar --iterations=5 'EXPRESSION'— for example:systemd-analyze calendar --iterations=5 'Mon..Fri 06:00'. -
sudo systemctl daemon-reload— without this, systemd uses the old (or non-existent) version of the unit file. -
journalctl -u myjob.service -n 50 --no-pagerfor recent logs, orjournalctl -u myjob.service -ffor live output. -
Type=oneshottells systemd the process is finite — it runs once and exits. This is the correct model for scheduled jobs. systemd won't start a second instance while the first is running. -
flock -n+RuntimeMaxSec=:ExecStart=/usr/bin/flock -n /var/lock/myjob.lock /usr/local/bin/myjob.shRuntimeMaxSec=2hflock -nprevents overlap.RuntimeMaxSeckills runaway jobs. -
sudo loginctl enable-linger "$USER"— this allows the user's systemd instance (and their timers) to keep running after logout.
What's Next
You've completed the full systemd timer documentation. Here are related topics:
- systemd.path — filesystem event-driven scheduling (complements timers)
- Cron — traditional time-based task scheduling
- Cron vs systemd Timers — comparison guide
- Automation Task Ranking — choosing the right automation tool