Why Use systemd When Docker Already Has a Restart Policy?
When I first started setting up lab environments, I was convinced that adding --restart always to my docker run command — or declaring restart: always in Docker Compose — was all I needed. If a container crashed, Docker would bring it back. If the server rebooted, the Docker daemon would restart the containers. Seemed fine. But in production, reality is a bit more complicated.
I once had a server lose power unexpectedly. When it came back up, the Docker daemon started before the network-attached storage (NFS) volumes had finished mounting. The result: containers failed to start because their data wasn’t there yet. Another time, I needed an application container to start only after a database installed directly on the host OS — not in Docker — was ready. For scenarios like these, Docker’s Restart Policy is essentially powerless.
That’s what pushed me toward using systemd to manage Docker containers. Systemd is the default system manager on most modern Linux distributions (Ubuntu, Debian, CentOS, and so on). By integrating Docker into systemd, containers are managed like any other OS service — with startup ordering, centralized logging via journald, and much easier monitoring.
Comparing Startup Management Methods
| Feature | Docker Restart Policy | Docker Compose | Systemd Integration |
|---|---|---|---|
| Ease of use | Very high | High | Medium |
| Dependency management | None | Only within the Compose file | Very powerful (works with any OS service) |
| Auto-restart | Yes | Yes | Yes (with more options) |
| Logging | Docker logs | Docker logs | Journald (centralized) |
Advantages of systemd
- Startup ordering (Dependencies): You can require a container to start only after the network, MySQL, or a specific disk is ready.
- Intelligent recovery: Systemd offers smarter restart behavior — for example, retry 5 times with 10 seconds between attempts, then stop entirely to avoid infinite crash loops.
- Monitoring integration: Tools like Zabbix and Prometheus can easily check service status via
systemctl.
Disadvantages
- Writing a unit configuration file is slightly more involved than typing a single command.
- Each container needs its own service file.
Deployment Guide: Turning a Container into a Systemd Service
Let’s say I have a simple web application running with Nginx. Instead of using docker run directly, I’ll create a service unit for it.
Step 1: Create the Unit File
Create a file with a .service extension in /etc/systemd/system/. I’ll name mine webapp.service.
sudo nano /etc/systemd/system/webapp.service
Step 2: Write the Configuration
This is the configuration I actually use for several personal VPS projects. Paste it into the file:
[Unit]
Description=My Web Application Container
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
Restart=always
# Remove old container if it exists before starting a new one
ExecStartPre=-/usr/bin/docker stop %p
ExecStartPre=-/usr/bin/docker rm %p
ExecStartPre=/usr/bin/docker pull nginx:latest
# Main startup command
ExecStart=/usr/bin/docker run --name %p \
-p 8080:80 \
nginx:latest
# Stop the container
ExecStop=/usr/bin/docker stop %p
[Install]
WantedBy=multi-user.target
Key parameters explained:
After=docker.service: Ensures the Docker daemon is running before this service starts.ExecStartPre=-/usr/bin/docker stop %p: The leading-means that if this command fails (e.g., the container doesn’t exist yet), systemd will continue to the next command rather than aborting.%p: A systemd specifier that expands to the service name without the extension (here:webapp). Used as the container name for easy identification.Restart=always: Automatically restarts the container if it crashes.
Step 3: Enable the Service
After saving the file, you need to tell systemd about the change and activate the service:
# Reload so the system recognizes the new file
sudo systemctl daemon-reload
# Enable the service to start with the OS
sudo systemctl enable webapp.service
# Start the service immediately
sudo systemctl start webapp.service
Step 4: Check the Status
Verify the container is running:
sudo systemctl status webapp.service
A green active (running) line means success. If you see failed, run journalctl -u webapp.service -n 50 to see the specific error.
Practical Tips: Log Management and Debugging
Once you switch to systemd, you no longer need docker logs. All container output flows into journald. The biggest win when debugging is having application logs and system logs on the same timeline — no more guessing the order of events.
Watch logs in real time:
journalctl -u webapp.service -f
When you need to deploy updated code or a new image, just run:
sudo systemctl restart webapp.service
Systemd automatically walks through the ExecStartPre steps — pulling the new image, removing the old container, and restarting. No need to remember the manual sequence.
When Should You NOT Use This Approach?
Systemd solves a specific problem well — but not every problem needs this solution.
- Development environments: Stick with Docker Compose for speed. Editing a service file and reloading the daemon every time you change a port is a waste of time.
- Complex microservices architectures: If you have dozens of interconnected containers, managing dozens of service files by hand will be a nightmare. Docker Compose or Kubernetes is the right call there.
This approach works best for single-instance services on a VPS — a Telegram Bot, a personal blog, or Nginx/Traefik as a reverse proxy. If you only have two or three independent containers, this is simpler and more stable than Docker Compose.
I’m running this exact setup for an Nginx reverse proxy and a Telegram Bot on my personal VPS — and for the past several months, I haven’t had to touch them manually once. If you’re dealing with containers that refuse to start after a reboot, give this a try.

