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:
/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.
Unit file search order (later wins):/usr/lib/systemd/system/ Installed by packages -- do not edit/etc/systemd/system/ Admin overrides -- edit theseComplete unit file anatomy:
[Unit]## Description shown in systemctl statusDescription=My Payment Service ## Start after these units are activeAfter=network.target postgresql.service redis.service ## If these fail, this unit fails tooRequires=postgresql.service ## If these fail, try to start anywayWants=redis.service [Service]## Run as this user and group (never root)User=payment-svcGroup=payment-svc ## Working directoryWorkingDirectory=/opt/payment-service ## The command to runExecStart=/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 restartingExecReload=/bin/kill -HUP $MAINPID ## Load environment variables from a file## This is how secrets stay out of unit filesEnvironmentFile=/etc/payment-service/env ## Or set individual environment variablesEnvironment=NODE_ENV=productionEnvironment=PORT=4000 ## Restart policy## on-failure: restart if exits with non-zero or killed by signalRestart=on-failureRestartSec=5 ## wait 5 seconds before restartingStartLimitInterval=60 ## within 60 secondsStartLimitBurst=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 limitsLimitNOFILE=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.targetOverriding package unit files without editing them:
## Create an override directorysudo mkdir -p /etc/systemd/system/nginx.service.d/ ## Create an override file -- only the sections you changesudo tee /etc/systemd/system/nginx.service.d/override.conf << 'EOF'[Service]LimitNOFILE=65536Restart=alwaysRestartSec=3EOF ## Reload and restartsudo systemctl daemon-reloadsudo systemctl restart nginx ## The easier way -- systemctl edit opens editor and creates overridesudo systemctl edit nginxPractical Commands
## Show full unit file contentsystemctl cat nginx ## Show all overrides appliedsystemctl cat nginx | head -5 ## first line shows which file ## Validate unit file syntax before applyingsystemd-analyze verify /etc/systemd/system/myapp.service ## Show unit file locationsystemctl show -p FragmentPath nginx ## List all unit files and their statesystemctl list-unit-files --type=serviceTroubleshooting
| 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 |
TipUse
systemd-analyze verify /etc/systemd/system/myapp.servicebefore 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.
SecurityNever put secrets (passwords, API keys) directly in unit files. Unit files are readable by any user with
systemctl cat. UseEnvironmentFile=/etc/service/envto 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.