Restart a Service If It Stopped

systemdmonitorcron-ready
5 min read

Quick Answer

The systemctl is-active command returns exit code 0 when a service is running and non-zero when it is stopped, failed, or not found. This makes it usable directly in a bash if statement without any output parsing. When the service is down, the script calls systemctl start and checks whether that command succeeded — two levels of verification rather than just attempting a restart and assuming it worked. The log file built with tee -a gives you an audit trail of every outage and recovery. The optional email alert sends different messages for recovery versus critical failure so you know whether the service came back up on its own. Without this watchdog, a crashed nginx or postgresql can stay down for hours until someone notices. Running the script every minute via cron — * * * * * /home/user/service-watchdog.sh — means maximum downtime before auto-recovery is under 60 seconds. Works with any systemd service on Ubuntu 22.04 LTS, Debian 12, Fedora 39, and CentOS 9.

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.

bash
#!/bin/bash CHECK="✓" CROSS="✗" # --- Configuration --- SERVICE="nginx" # Change to your service name LOG_FILE="/var/log/service-watchdog.log" DATE=$(date '+%Y-%m-%d %H:%M:%S') NOTIFY_EMAIL="" # Optional: you@example.com # --- Check if service is running --- if systemctl is-active --quiet "$SERVICE"; then echo "$CHECK [$DATE] $SERVICE is running" else echo "$CROSS [$DATE] $SERVICE is NOT running — attempting restart..." echo "$CROSS [$DATE] $SERVICE DOWN — restarting" >> "$LOG_FILE" # --- Attempt restart --- if sudo systemctl start "$SERVICE"; then echo "$CHECK [$DATE] $SERVICE restarted successfully" | tee -a "$LOG_FILE" # --- Optional: send email notification --- if [ -n "$NOTIFY_EMAIL" ]; then echo "$SERVICE was down and has been restarted on $(hostname) at $DATE" \ | mail -s "[RECOVERED] $SERVICE restarted" "$NOTIFY_EMAIL" fi else echo "$CROSS [$DATE] $SERVICE FAILED to restart — manual intervention needed" \ | tee -a "$LOG_FILE" if [ -n "$NOTIFY_EMAIL" ]; then echo "$SERVICE failed to restart on $(hostname) at $DATE. Check: journalctl -u $SERVICE" \ | mail -s "[CRITICAL] $SERVICE restart failed" "$NOTIFY_EMAIL" fi fi fi

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

bash
nano service-watchdog.sh

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:

bash
# Find the correct name for your service systemctl list-units --type=service | grep -i nginx systemctl list-units --type=service | grep -i mysql

Common service names:

SoftwareService name
nginxnginx
Apacheapache2 (Debian/Ubuntu) or httpd (RHEL/CentOS)
MySQLmysql or mysqld
PostgreSQLpostgresql
Redisredis or redis-server
SSH daemonssh or sshd

Step 3 — Make it executable and run a manual test

bash
chmod +x service-watchdog.sh ./service-watchdog.sh

When the service is running, you should see:

text
✓ [2026-06-03 14:30:01] nginx is running

To test the restart path, stop the service first:

bash
sudo systemctl stop nginx ./service-watchdog.sh

You should see the ✗ DOWN — restarting line followed by ✓ restarted successfully. Then confirm nginx is back:

bash
systemctl is-active nginx # active

Step 4 — Set up email alerts (optional)

Edit NOTIFY_EMAIL="" at the top of the script and add your address:

bash
NOTIFY_EMAIL="you@example.com"

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:

bash
crontab -e

Add this line (use your actual path):

bash
# Check every minute — maximum downtime before auto-recovery is 60 seconds * * * * * /home/user/service-watchdog.sh >> /var/log/watchdog-cron.log 2>&1

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

CommandWhat it does
systemctl is-active --quietExit 0 only when the unit is active; gates the restart path
sudo systemctl startAttempts to bring the stopped unit back to active state
tee -aMirror stdout to terminal while appending to LOG_FILE
mailOptional notifications when NOTIFY_EMAIL is set
journalctl -u SERVICEInspect 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 →

BashSnippets logo

Written by Anguishe

Creator of BashSnippets.xyz

bashsnippets.xyz/about

Run this script on a real Linux server

Get $200 free credit — DigitalOcean

Get $200 Free →

Affiliate link · we earn a commission

Need a domain for your next project?

Register with Namecheap — free WHOIS privacy included

Check Domain Prices →

Affiliate link · we earn a commission

Related Snippets

Frequently Asked Questions

How do I run this script?

Save as service-watchdog.sh, set SERVICE name, run chmod +x service-watchdog.sh, then execute ./service-watchdog.sh.

Does this work on macOS?

No. systemctl is Linux systemd only. macOS uses launchd — use launchctl list instead.

How do I check if a service is running in bash?

Run systemctl is-active servicename. Exit code 0 means running; non-zero means stopped or failed.

How do I restart a service automatically in Linux?

Wrap systemctl is-active and systemctl start in an if statement, then schedule the script with cron every minute.