Skip to main content
โšก Calmops

Caddy Systemd Service Restarts Forever: caddy run vs caddy start

The Problem

If your Caddy service keeps restarting in a loop with no error logs, the cause is almost certainly using caddy start instead of caddy run in your systemd unit file.

Symptoms:

  • systemctl status caddy shows the service cycling through active โ†’ activating โ†’ active
  • journalctl -u caddy -f shows repeated start messages but no errors
  • The web server works briefly, then restarts again

Root Cause: start vs run

Caddy has two commands for starting the server:

caddy run    # Starts Caddy and BLOCKS (foreground process)
caddy start  # Starts Caddy in the BACKGROUND and returns immediately

Systemd expects ExecStart to run a foreground process that stays alive. When you use caddy start, the command launches Caddy in the background and then exits immediately with code 0. Systemd sees the process exit and โ€” because Restart=always is set โ€” restarts it. This creates an infinite loop.

The Fix

Change caddy start to caddy run in your service file:

# /etc/systemd/system/caddy.service  (BROKEN)
[Unit]
Description=Caddy Web Server
After=network.target

[Service]
User=root
Group=root
Type=simple
Restart=always
RestartSec=15s
WorkingDirectory=/root/caddy
ExecStart=/root/caddy/caddy start   # <-- WRONG: exits immediately

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/caddy.service  (FIXED)
[Unit]
Description=Caddy Web Server
After=network.target

[Service]
User=root
Group=root
Type=simple
Restart=always
RestartSec=15s
WorkingDirectory=/root/caddy
ExecStart=/root/caddy/caddy run     # <-- CORRECT: blocks indefinitely

[Install]
WantedBy=multi-user.target

After editing the file:

sudo systemctl daemon-reload
sudo systemctl restart caddy
sudo systemctl status caddy

A Better Production Caddy Service File

The official Caddy project provides a well-configured service file. Here’s a hardened version:

# /etc/systemd/system/caddy.service
[Unit]
Description=Caddy Web Server
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Key improvements over the minimal version:

Setting Purpose
Type=notify Caddy signals systemd when it’s ready (more reliable than simple)
ExecReload=caddy reload Graceful config reload without downtime
LimitNOFILE=1048576 Raise file descriptor limit for high-traffic sites
PrivateTmp=true Isolate /tmp for security
ProtectSystem=full Read-only access to /usr, /boot, /etc
AmbientCapabilities=CAP_NET_BIND_SERVICE Allow binding to ports 80/443 as non-root
Restart=on-failure Only restart on failure, not on clean exit

Using the Official Package

If you installed Caddy via the official package (apt/rpm), the service file is already correct:

# Install via apt (Debian/Ubuntu)
sudo apt install caddy

# The package includes a correct service file at:
# /lib/systemd/system/caddy.service

sudo systemctl enable caddy
sudo systemctl start caddy

Reloading Config Without Restart

One advantage of caddy run with systemd is zero-downtime config reloads:

# Reload Caddy config without dropping connections
sudo systemctl reload caddy

# Or directly
caddy reload --config /etc/caddy/Caddyfile

This is much better than restart, which briefly drops all connections.

Debugging Systemd Services

Useful commands when troubleshooting any systemd service:

# View service status
systemctl status caddy

# Follow live logs
journalctl -u caddy -f

# View last 50 lines of logs
journalctl -u caddy -n 50

# View logs since last boot
journalctl -u caddy -b

# Check if service is enabled on boot
systemctl is-enabled caddy

# List all failed services
systemctl --failed

# Show full service file (including overrides)
systemctl cat caddy

Resources

Comments