This script handles one of the three kinds of “down”
systemctl is-active tells you whether the unit is running. It does not tell you whether the service is answering, whether someone stopped it on purpose, or why systemctl start is refusing with "start request repeated too quickly". The full guide covers all three, the exit code 4 that makes a watchdog restart a unit that does not exist, and a watchdog that alerts once per outage: Auto-Restart a Stopped Service on Linux.
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. A service watchdog is one of the core jobs in the bash scripts every sysadmin needs guide.
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.
For watchdogs that restart services on a remote host over SSH, the SSH key setup script enables passwordless key-based authentication so the cron user can connect without a password prompt.
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.
A watchdog that restarts a service is one crash-loop away from hiding the real problem. Bounding the restart count, logging every intervention with a timestamp, and escalating when the count climbs is the difference between a watchdog and a cover-up. The Production Bash Toolkit implements that logic once, as a shared library, rather than per service.
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. If services crash repeatedly due to resource exhaustion, add CPU and RAM monitoring to identify the root cause.
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. After restarting, confirm the HTTP endpoint responds correctly with the check if website is up script — a service that starts but returns 500 needs a different fix.
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.
Part of the bash snippets collection