systemd Unit Files (.service)
A systemd unit file is a plain text file that declaratively defines a system component such as a service, timer, or mount point. To register your own application as a daemon, create a .service file and describe its behavior in three sections: [Unit], [Service], and [Install]. Once registered, you can manage startup, shutdown, and automatic launching with the systemctl command.
Syntax
# =====================================================
# Basic structure of a .service file
# =====================================================
#
# Place the file at /etc/systemd/system/{service-name}.service
# The order of sections ([Unit] / [Service] / [Install])
# follows convention but does not affect behavior
# =====================================================
[Unit]
# Human-readable description (shown in journalctl and systemctl status)
Description={description of the service}
# Units that must be started before this service is started
After={unit name}
# Hard dependency: if the specified unit fails to start, this service will not start either
Requires={unit name}
# Soft dependency: this service will still attempt to start even if the specified unit fails
Wants={unit name}
[Service]
# Command to launch the main process (specify the full path)
ExecStart={full path to command} {arguments ...}
# Command to run when the service is stopped (optional)
ExecStop={full path to command} {arguments ...}
# Command to run after the service stops (e.g., cleanup tasks)
ExecStopPost={full path to command} {arguments ...}
# User to run the service as (non-root is recommended)
User={username}
# Group to run the service as (optional)
Group={group name}
# Working directory for the process
WorkingDirectory={full path to directory}
# Set environment variables directly (multiple lines allowed)
Environment="VARIABLE_NAME=value"
# Load environment variables from an external file
EnvironmentFile={full path to .env file}
# Process startup type
# simple : treats the process started by ExecStart as the main process (default)
# forking : for traditional daemons that fork and move to the background
# notify : for services that signal readiness via sd_notify()
# oneshot : for tasks that run once and exit
Type={simple|forking|notify|oneshot}
# Conditions under which the service should be automatically restarted
# no : do not restart (default)
# always : always restart
# on-failure : restart only on abnormal exit
Restart={no|always|on-failure|on-abnormal|on-success}
# How long to wait before restarting (default: 100ms)
RestartSec={seconds}
[Install]
# Target to attach this service to when running systemctl enable
# multi-user.target is the standard multi-user mode (after network is up)
WantedBy={target name}
Directive Reference
| Section | Directive | Description |
|---|---|---|
| [Unit] | Description | A human-readable description of the service. Displayed in the header of systemctl status and journalctl output. |
After | Controls startup ordering. This service starts after the specified unit has started. Common values include network.target and mysql.service. | |
Requires | Declares a hard dependency. If the specified unit fails to start, this service will not start either. | |
Wants | Declares a soft dependency. This service will still attempt to start even if the specified unit fails. | |
| [Service] | ExecStart | The command to start the service. This is required, and the command must be specified as a full path. |
ExecStop | The command to run when the service is stopped. If omitted, a SIGTERM signal is sent. | |
Restart | The restart policy. Setting on-failure enables automatic recovery after a crash. | |
User / Group | Specifies the user and group to run the service as. Using a dedicated non-root user improves security. | |
WorkingDirectory | The working directory for the process. Required when the command uses relative paths. | |
Environment | Sets environment variables inline. Written as Environment="NODE_ENV=production". | |
EnvironmentFile | Loads environment variables from an external file. Specify the full path to a .env file. | |
Type | The service startup type. Use simple for a long-running process, and forking for a traditional daemon that forks. | |
| [Install] | WantedBy | Specifies which target to attach this service to when running systemctl enable. Typically set to multi-user.target. |
Examples
/etc/systemd/system/kyo-app.service
# ===================================================== # Register a Node.js web app as a systemd service # The app runs persistently under the kyo user # ===================================================== [Unit] # Description shown in journalctl and systemctl status Description=Kyo Kusanagi Web Application # Start after the network is available After=network.target [Service] # Run the service as the dedicated kyo user # Avoid running as root to reduce security risk User=kyo Group=kyo # Set the working directory to where the app source code lives WorkingDirectory=/home/kyo/kyo-app # Inform the app that it is running in production Environment="NODE_ENV=production" Environment="PORT=3000" # Load secrets such as the secret key from a separate file # /home/kyo/kyo-app/.env should contain entries like SECRET_KEY=... EnvironmentFile=/home/kyo/kyo-app/.env # Specify the Node.js startup command using its full path ExecStart=/usr/bin/node /home/kyo/kyo-app/server.js # Automatically restart on abnormal exit (crash) # A clean exit (exit 0) will not trigger a restart Restart=on-failure # Wait 5 seconds before restarting (prevents rapid restart loops on repeated crashes) RestartSec=5 [Install] # When systemctl enable is run, attach this service to multi-user.target # so it starts automatically at boot in normal multi-user mode WantedBy=multi-user.target
Run the following command:
$ sudo systemctl daemon-reload
$ sudo systemctl enable kyo-app.service
Created symlink /etc/systemd/system/multi-user.target.wants/kyo-app.service → /etc/systemd/system/kyo-app.service.
$ sudo systemctl start kyo-app.service
$ sudo systemctl status kyo-app.service
● kyo-app.service - Kyo Kusanagi Web Application
Loaded: loaded (/etc/systemd/system/kyo-app.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2026-03-25 12:00:00 JST; 3s ago
Main PID: 12480 (node)
Tasks: 11 (limit: 4915)
Memory: 38.2M
CPU: 412ms
CGroup: /system.slice/kyo-app.service
└─12480 /usr/bin/node /home/kyo/kyo-app/server.js
/etc/systemd/system/iori-worker.service
# ===================================================== # Register a Python background worker as a systemd service # The worker continuously pulls jobs from a queue and processes them # ===================================================== [Unit] Description=Iori Yagami Background Worker # Start after both the network and the Redis service are up After=network.target redis.service # Declare a hard dependency on Redis since the worker cannot function without it Requires=redis.service [Service] User=iori Group=iori WorkingDirectory=/opt/iori-worker # When using a Python virtual environment, point to the python binary inside venv ExecStart=/opt/iori-worker/venv/bin/python worker.py # Load REDIS_URL, DATABASE_URL, and other variables from the environment file EnvironmentFile=/opt/iori-worker/.env # Always restart regardless of exit status (keep the worker running at all times) Restart=always RestartSec=3 [Install] WantedBy=multi-user.target
Run the following command:
$ sudo systemctl daemon-reload $ sudo systemctl enable --now iori-worker.service Created symlink /etc/systemd/system/multi-user.target.wants/iori-worker.service → /etc/systemd/system/iori-worker.service. $ sudo journalctl -u iori-worker.service -f Mar 25 12:05:00 server iori-worker[12600]: Worker started. Waiting for jobs... Mar 25 12:05:12 server iori-worker[12600]: [JOB] Processing: render_flame_effect Mar 25 12:05:12 server iori-worker[12600]: [JOB] Done: render_flame_effect (0.83s)
Stopping, disabling, and removing a service
# ----------------------------------------------- # Stop the service and disable it # ----------------------------------------------- # Stop the service (the process exits, but the unit file remains) sudo systemctl stop kyo-app.service # Disable automatic startup (removes the symlink created by enable) sudo systemctl disable kyo-app.service # ----------------------------------------------- # Remove the unit file to fully unregister the service # ----------------------------------------------- # Delete the unit file itself sudo rm /etc/systemd/system/kyo-app.service # Notify systemd of the file changes # Always run this after adding, editing, or deleting unit files sudo systemctl daemon-reload # Clear any leftover state from the removed service sudo systemctl reset-failed
Run the following command:
$ sudo systemctl daemon-reload $ sudo systemctl reset-failed $ systemctl status kyo-app.service Unit kyo-app.service could not be found.
Overview
systemd unit files let you manage your own applications and scripts as first-class system services. A .service file placed in /etc/systemd/system/ defines startup ordering and dependencies in the [Unit] section, the run command, user, and restart policy in the [Service] section, and the boot-time target in the [Install] section. After creating or editing a file, always run sudo systemctl daemon-reload to apply the changes. Use the systemctl command to start, stop, and check the status of services, and journalctl to view logs. For recurring tasks, you can pair a .service file with a systemd timer. As a best practice, avoid running services as root — use a dedicated non-privileged user instead — and keep secrets out of the unit file by loading them via EnvironmentFile. This improves both security and maintainability.
If you find any errors or copyright issues, please contact us.