Skip to main content

Unit File

A systemd unit file is a configuration file that describes a service, socket, timer, or other system resource managed by systemd. Unit files have three sections: [Unit] for metadata and dependencies, [Service] for execution details, and [Install] for boot-time configuration.

What Is a Unit File in Simple Terms

A unit file is a recipe card for systemd. It tells systemd everything it needs to know about a service: what to run, who to run it as, what it depends on, what to do if it crashes, and whether to start it at boot.

Before systemd, starting a service required writing a shell script with dozens of lines handling process management, PID files, logging, and restart logic. A systemd unit file does all of that in 15-20 lines of declarative configuration.

Unit file structure:

◈ DIAGRAM
/etc/systemd/system/payment-service.service
+----------------------------------------+
| [Unit] |
| Description=Payment Processing API |
| After=postgresql.service |
+----------------------------------------+
| [Service] |
| User=payment-svc |
| ExecStart=/usr/bin/node index.js |
| EnvironmentFile=/etc/payment/env |
| Restart=on-failure |
| RestartSec=5 |
+----------------------------------------+
| [Install] |
| WantedBy=multi-user.target |
+----------------------------------------+

How It Works

Unit files are INI-style configuration with three sections. systemd reads them from specific directories, with later directories overriding earlier ones.

TEXT
Unit file search order (later wins):
/usr/lib/systemd/system/ Installed by packages -- do not edit
/etc/systemd/system/ Admin overrides -- edit these

Complete unit file anatomy:

INI
[Unit]
## Description shown in systemctl status
Description=My Payment Service
## Start after these units are active
After=network.target postgresql.service redis.service
## If these fail, this unit fails too
Requires=postgresql.service
## If these fail, try to start anyway
Wants=redis.service
[Service]
## Run as this user and group (never root)
User=payment-svc
Group=payment-svc
## Working directory
WorkingDirectory=/opt/payment-service
## The command to run
ExecStart=/usr/bin/node /opt/payment-service/index.js
## Command to gracefully stop (optional -- default is SIGTERM)
ExecStop=/bin/kill -TERM $MAINPID
## Command to reload config without restarting
ExecReload=/bin/kill -HUP $MAINPID
## Load environment variables from a file
## This is how secrets stay out of unit files
EnvironmentFile=/etc/payment-service/env
## Or set individual environment variables
Environment=NODE_ENV=production
Environment=PORT=4000
## Restart policy
## on-failure: restart if exits with non-zero or killed by signal
Restart=on-failure
RestartSec=5 ## wait 5 seconds before restarting
StartLimitInterval=60 ## within 60 seconds
StartLimitBurst=3 ## allow max 3 restarts before giving up
## Service type
## simple: ExecStart process IS the service (most common)
## forking: service forks and the parent exits (old-style daemons)
## oneshot: runs once and exits (good for setup scripts)
Type=simple
## File descriptor limits
LimitNOFILE=65536 ## override ulimit for this service
[Install]
## Which target to start this service in
## multi-user.target = standard server mode (no GUI)
WantedBy=multi-user.target

Overriding package unit files without editing them:

Bash
## Create an override directory
sudo mkdir -p /etc/systemd/system/nginx.service.d/
## Create an override file -- only the sections you change
sudo tee /etc/systemd/system/nginx.service.d/override.conf << 'EOF'
[Service]
LimitNOFILE=65536
Restart=always
RestartSec=3
EOF
## Reload and restart
sudo systemctl daemon-reload
sudo systemctl restart nginx
## The easier way -- systemctl edit opens editor and creates override
sudo systemctl edit nginx

Practical Commands

Bash
## Show full unit file content
systemctl cat nginx
## Show all overrides applied
systemctl cat nginx | head -5 ## first line shows which file
## Validate unit file syntax before applying
systemd-analyze verify /etc/systemd/system/myapp.service
## Show unit file location
systemctl show -p FragmentPath nginx
## List all unit files and their state
systemctl list-unit-files --type=service

Troubleshooting

Symptom Command What to Look For
Unit file change ignored sudo systemctl daemon-reload Must reload after any file edit
Service exits immediately journalctl -u service -n 20 Exit code and error message
Wrong user running service systemctl show service -p User User directive in unit file
Environment not loading systemctl show service -p EnvironmentFiles Path and whether file exists
Tip

Use systemd-analyze verify /etc/systemd/system/myapp.service before reloading. It catches syntax errors in unit files before you try to start the service. A syntax error can make the service silently do nothing or fail to load.

Security

Never put secrets (passwords, API keys) directly in unit files. Unit files are readable by any user with systemctl cat. Use EnvironmentFile=/etc/service/env to load secrets from a file with restricted permissions (chmod 640, chown root:service-group). The environment file is not readable by ordinary users but is loaded by systemd before starting the service.

Frequently Asked Questions

What actually goes in the [Service] section that makes a unit file more than just a start command?

[Service] defines restart behavior (`Restart=on-failure`, `RestartSec=5`), the process type (`Type=simple`, `forking`, `oneshot`, `notify`), resource limits (`MemoryMax`, `CPUQuota`), and the user/group to run as — control that a plain shell script or cron entry doesn't give you. `Type=notify` in particular lets a service tell systemd exactly when it's fully ready via sd_notify, so dependent units don't start prematurely.

What's a common mistake when writing a custom systemd unit file?

Forgetting `WantedBy=multi-user.target` in [Install], which means `systemctl enable` silently does nothing useful — the service won't start on boot even though it looks enabled. Another frequent gotcha: not setting `Restart=` at all, so a crashing service just stays dead until someone notices, versus `Restart=on-failure` which at least attempts recovery. Always run `systemctl daemon-reload` after editing a unit file — systemd caches unit definitions and won't pick up edits otherwise.