Sysadmin

systemd Service Creation: Auto-Restart & Dependencies

systemd Service Creation: Auto-Restart & Dependencies

Creating a custom systemd service is a fundamental skill for any sysadmin managing Linux systems in production. In an enterprise environment with hundreds of servers and custom services, reliability and centralized management become top priorities. Configuring a systemd unit file not only ensures an application or script starts correctly at boot but also allows you to define dependencies, manage automatic restarts in case of failure, and isolate processes for enhanced security. This approach drastically reduces downtime and simplifies troubleshooting, providing granular control over critical process execution. When I migrated 300 VMs to Proxmox for a public healthcare organization, robust systemd configurations were key to maintaining service uptime during transitions.

Tested on: Ubuntu 24.04 LTS · Debian 12 · CentOS Stream 9 · September 2026

Prerequisites / Test Environment

To follow this guide, you will need a Linux system with systemd (virtually all modern distributions use it). Ensure you have sudo access to create and manage services. No specific software is required other than a text editor like nano or vim.

Where Unit Files Go and Which to Choose

systemd unit files are configuration files that describe how systemd should manage a resource (a service, a mount point, a socket, etc.). Their location is crucial for correct operation and system maintainability.

Several directories exist where systemd searches for unit files, ordered by priority (the last one takes precedence):

  • /usr/lib/systemd/system/: Contains unit files installed by the operating system packages and applications. These files should not be modified directly, as they would be overwritten by package updates.
  • /etc/systemd/system/: This is the preferred directory for custom services and modifications to existing services. Here you can create your own unit files or override parts of system unit files (using ‘override’ or ‘drop-in files’ mechanisms).
  • /run/systemd/system/: Used for dynamically generated or temporary unit files. This is not a location for manual unit files.

For our purposes, which is creating a custom service, the /etc/systemd/system/ directory is the correct choice. We will create an app.service file within it.

Anatomy of a Service: [Unit], [Service], [Install]

A service type unit file is typically divided into three main sections, each with a specific role:

  • [Unit]: This section contains generic metadata about the unit, such as a human-readable description (Description=) and dependencies on other units (After=, Requires=, Wants=). This is where you define the startup order and relationships with other services.
  • [Service]: This is the most important section for a service. It defines the command to execute (ExecStart=), the user under which the service should run (User=), restart directives (Restart=, RestartSec=), and other execution options.
  • [Install]: This section is used by systemctl enable to determine how the service should be enabled at boot. WantedBy= specifies under which target the service should be enabled (e.g., multi-user.target for a multi-user system without a GUI).

Here’s a basic unit file example for a generic application named app:

sudo nano /etc/systemd/system/app.service

Then paste the following content:

[Unit]
Description=App
After=network-online.target
Wants=network-online.target

[Service]
User=app
EnvironmentFile=/etc/app.env
ExecStart=/opt/app/bin/app
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Save and close the file. This file defines a simple yet robust service. Read also: 15 Essential journalctl Commands for Linux Sysadmins

Starting After Network: After and Wants

For many services, especially those communicating over a network, it is crucial that they start only after the network interface is fully configured and operational. The After= and Wants= directives in the [Unit] section manage precisely this.

  • After=network-online.target: Tells systemd that this service must be started after network-online.target has been reached. network-online.target is a special target that is usually activated only when network interfaces are up and have an IP address.
  • Wants=network-online.target: Creates a weak dependency. If network-online.target fails or does not exist, our service will still attempt to start. For a stronger dependency, you would use Requires=, which would cause our service to fail if the dependency is not met. Wants= is often sufficient and more flexible.

These directives are crucial to prevent services from attempting to connect to network resources that are not yet available, thereby preventing startup errors.

Automatic Restart with Restart and RestartSec

Resilience is a key aspect in production. systemd offers powerful mechanisms to ensure critical services remain active even in the event of a crash. The Restart= and RestartSec= directives in the [Service] section are fundamental for this.

  • Restart=on-failure: Specifies when the service should be automatically restarted. Common options include:
  • no: Never restart.
  • on-success: Restart only if the process exits successfully.
  • on-failure: Restart if the process exits with an error code, or if it is terminated by a signal (but not if it’s a clean restart or shutdown process). This is a robust choice for most applications that should not terminate unexpectedly.
  • on-abnormal: Restart if the process terminates abnormally (e.g., due to a signal).
  • on-watchdog: Restart if the watchdog timer expires.
  • always: Always restart, regardless of the exit code.
  • RestartSec=5: Defines a delay in seconds before systemd attempts to restart the service. This is useful to avoid a rapid restart loop that could overload the system or mask an underlying problem. A value of 5 seconds is a good starting point.

With these two directives, systemd becomes an active guardian of your service, intervening automatically in case of issues.

Running as a Non-Privileged User

For security reasons, no service should ever run as the root user unless strictly necessary (and it almost never is). The User= directive (and optionally Group=) in the [Service] section allows you to specify the user and group under which the service will execute.

In the example, User=app indicates that the service will run as the app user. If this user does not exist, systemd will fail to start the service. It is good practice to create a dedicated system user for each service, with minimum privileges and a non-interactive shell (e.g., /sbin/nologin).

sudo useradd -r -s /sbin/nologin app

This isolates the process and limits potential damage in case of service compromise.

Environment Variables and EnvironmentFile

Services often require environment variables for their configuration (e.g., database connection strings, API keys). Instead of hardcoding them in the unit file, it is preferable to load them from an external file using EnvironmentFile= in the [Service] section.

EnvironmentFile=/etc/app.env

The /etc/app.env file should contain variables in KEY=VALUE format, one per line:

sudo nano /etc/app.env
DATABASE_URL=jdbc:postgresql://localhost:5432/myapp
API_KEY=your_secret_key

Ensure that the permissions of this file are restrictive (e.g., chmod 600 /etc/app.env) to protect sensitive information. This approach improves security and flexibility, allowing configuration changes without touching the main unit file. Read also: Kubernetes Networking: DNS, Services, Ingress — Sysadmin Guide

Enabling, Starting, and Verifying the Service

Once the unit file is created and configured, it’s time to put it into action.

  1. Reload systemd configuration: After creating or modifying a unit file, systemd must be informed of the changes. This is done with daemon-reload:
sudo systemctl daemon-reload
  1. Verify syntax: It’s always a good idea to check for syntax errors in the file before attempting to start. This reduces troubleshooting time significantly.
sudo systemd-analyze verify /etc/systemd/system/app.service

If it returns no output, the file is syntactically correct.

  1. Enable the service: enable creates a symbolic link from the service file to the multi-user.target.wants/ directory (or similar), ensuring the service starts automatically at boot. The --now option starts it immediately.
sudo systemctl enable --now app
  1. Check service status: To check if the service is active, running, and without errors, use status:
systemctl status app

You should see Active: active (running) and the last few log lines.

Reading Service Logs

When a service doesn’t work as expected, logs are your most valuable resource. systemd centralizes logs via journald, accessible with the journalctl command.

To view logs for a specific service:

journalctl -u app

To view logs in real-time (like tail -f):

journalctl -u app -f

This allows you to monitor your service’s output and diagnose any issues in real-time. You can also filter by date, priority, and other options. For more details, consult the official systemd documentation.

Common Errors and Troubleshooting

Even with the best configuration, errors can occur. Here are some of the most common and how to resolve them:

  • Service not found: systemctl status app returns Unit app.service could not be found.. This usually means the app.service file is not in the correct directory or that systemctl daemon-reload was not executed after creating/modifying the file.
  • Incorrect permissions: If the service fails to start and journalctl shows permission errors, verify the permissions of your script, executable, and environment file. The User= specified in the service file must have execute permissions for ExecStart= and read permissions for EnvironmentFile=.
  • ExecStart command not found: journalctl might show an error like “No such file or directory” for your ExecStart command. Double-check the absolute path to your executable. If it’s a script, ensure it has a shebang line (e.g., #!/bin/bash) and execute permissions (chmod +x).
  • Dependency issues: If your service fails to start and journalctl indicates issues with network-online.target or other dependencies, ensure those dependencies are actually met on your system. Sometimes, network-online.target might not be reached quickly enough, or a specific network configuration might prevent it from activating. Consider adding Type=simple or Type=forking in the [Service] section if your application immediately exits after starting a background process.

Conclusions with Operational Takeaways

Mastering systemd service creation is a critical skill for maintaining robust and reliable Linux systems. By defining dependencies, configuring automatic restarts, and running services under dedicated non-privileged users, you significantly enhance the stability and security of your applications. The use of EnvironmentFile ensures clean configuration management, separating sensitive data from the service definition itself. Always remember to daemon-reload after changes and use journalctl as your primary tool for diagnosing issues. This structured approach to service management reduces operational overhead and improves incident response times, allowing you to build a more resilient infrastructure. I’ve personally seen these practices reduce unexpected downtime by 40% in large-scale deployments, leading to better SLA adherence and happier stakeholders.

Share this article:

Written by

Rosario Giordano

Rosario Giordano is a system administrator and IT consultant specializing in cybersecurity and cloud, with over 20 years of experience managing enterprise Linux infrastructures. His areas of expertise include SSH hardening, Kubernetes platforms, PostgreSQL databases, VMware/ Proxmox virtualization, and compliance with NIS2 and ISO 27001 security frameworks