The Script
Save as service-watchdog.sh. Set SERVICE to any unit from systemctl list-units --type=service. If you use sudo systemctl start inside the script (as below), configure passwordless sudo for that command for your cron user, or run the watchdog as root.
Schedule this as a 1-minute cron job
-
-
-
-
- /home/user/service-watchdog.sh To suppress cron email output:
-
-
-
-
-
-
-
- /home/user/service-watchdog.sh >> /var/log/watchdog-cron.log 2>&1
-
-
-
Cron and sudo
Cron runs with a minimal environment and no TTY. If sudo systemctl start prompts for a password, the job fails silently. Prefer a sudoers rule for your cron user (NOPASSWD for /bin/systemctl start yourservice) or run without sudo when the cron user already has permission to manage the unit.
How It Works
systemctl is-active --quiet "$SERVICE" asks systemd whether the unit is active. With --quiet, there is no output—only an exit status: 0 means running (script prints the ✓ path and exits), anything else means inactive, failed, or missing (non-zero), so the script enters the restart branch.
When the service is down, the script logs a line by appending to LOG_FILE. On success, echo ... | tee -a "$LOG_FILE" prints the recovered message and appends it to the log—the operator sees immediate feedback while cron still has an audit trail.
Failure handling is two levels deep: first, is the unit running? Second, did systemctl start succeed? If the restart command fails (bad unit name, dependency error, masked service), if sudo systemctl start is false—you get ✓/recovery email only after a verified start; otherwise the script emits a ✗ critical message, logs via tee -a, and sends a [CRITICAL] email pointing you at journalctl -u $SERVICE instead of implying the service is healthy.
Step-by-Step Setup
Step 1 — Create the script file
Paste the script above, then press Ctrl+X → Y → Enter to save.
Step 2 — Set your service name
Edit SERVICE="nginx" to the name of the service you want to monitor. The name must match exactly what systemctl uses:
Common service names:
| Software | Service name |
|---|---|
| nginx | nginx |
| Apache | apache2 (Debian/Ubuntu) or httpd (RHEL/CentOS) |
| MySQL | mysql or mysqld |
| PostgreSQL | postgresql |
| Redis | redis or redis-server |
| SSH daemon | ssh or sshd |
Step 3 — Make it executable and run a manual test
When the service is running, you should see:
To test the restart path, stop the service first:
You should see the ✗ DOWN — restarting line followed by ✓ restarted successfully. Then confirm nginx is back:
Step 4 — Set up email alerts (optional)
Edit NOTIFY_EMAIL="" at the top of the script and add your address:
The script sends two different emails: one when the service recovers ([RECOVERED]) and one when the restart itself fails ([CRITICAL]). The [CRITICAL] email includes the journalctl command to run so you can investigate immediately.
Step 5 — Schedule with cron every minute
Open your crontab:
Add this line (use your actual path):
The >> /var/log/watchdog-cron.log 2>&1 redirect captures all output so you have a log of every check, every outage, and every recovery.
Cron and sudo permissions
If sudo systemctl start prompts for a password inside cron, the restart fails silently. Add a targeted NOPASSWD rule for your cron user: echo 'cronuser ALL=(ALL) NOPASSWD: /bin/systemctl start nginx' | sudo tee /etc/sudoers.d/watchdog. Replace cronuser and nginx with your actual user and service name.
Variations
Watch multiple services
Use a Bash array of unit names and loop: for each element, call systemctl is-active --quiet "$svc" and restart on failure—the same logic as above, reused per service.
Use systemd’s own watchdog semantics
For long-running apps you control, a unit drop-in can set Restart=on-failure (and related options) so systemd restarts the process without cron. Cron still helps when you want mail, extra logging, or cross-checks across several units.
Slack or webhook alerts
Replace the mail pipeline with curl -X POST -H 'Content-Type: application/json' to your Incoming Webhook URL, sending JSON with hostname, timestamp, and service name for recover vs. failure.
Understanding the Commands
| Command | What it does |
|---|---|
systemctl is-active --quiet | Exit 0 only when the unit is active; gates the restart path |
sudo systemctl start | Attempts to bring the stopped unit back to active state |
tee -a | Mirror stdout to terminal while appending to LOG_FILE |
mail | Optional notifications when NOTIFY_EMAIL is set |
journalctl -u SERVICE | Inspect why a restart failed (logs from systemd) |
Common Mistakes
Cron and sudo
Cron runs with a minimal environment and no TTY. If sudo systemctl start prompts for a password, the job fails silently. Add a sudoers rule for the cron user: cron_user ALL=(ALL) NOPASSWD: /bin/systemctl start servicename.
Frequently Asked Questions
How do I check if a service is running in bash?
Use systemctl is-active: systemctl is-active --quiet nginx returns exit code 0 if running, non-zero if stopped. Use in an if statement: if systemctl is-active --quiet nginx; then ...
How do I restart a service automatically in Linux?
Write a bash script that checks systemctl is-active and runs systemctl start if the service is stopped. Schedule it every minute with cron: * * * * * /home/user/watchdog.sh
What services can I monitor with this script?
Any systemd service: nginx, apache2, mysql, postgresql, redis, docker, ssh, and custom services. Change SERVICE='nginx' to any service name from systemctl list-units.
How do I run a bash script every minute with cron?
Open crontab -e and add: * * * * * /full/path/to/script.sh — the five asterisks mean "every minute of every hour of every day".
What is systemctl is-active?
systemctl is-active servicename checks whether a systemd service is currently running. It returns exit code 0 (success) if active, and 1+ (error) if inactive, failed, or not found.
Try the Generator →