How to Create a systemd Service on Linux
Updated Jun 2026 · originally published Jun 2026 · Tested on Ubuntu 24.04, Debian 12, RHEL 9
To run your own program as a managed Linux service — started at boot, restarted on failure, with its logs in the journal — you write a systemd unit file. This guide builds one from scratch and explains every directive.
Where unit files live
Unit files exist in a priority order:
/etc/systemd/system/— your custom and override units (highest priority)/run/systemd/system/— runtime units, generated dynamically/lib/systemd/system/— units installed by packages (lowest priority)
Put your own services in /etc/systemd/system/. That keeps them separate from
package-managed units and safe from being overwritten by updates.
A complete service unit
Here’s a production-ready unit for a Node.js app. Save it as
/etc/systemd/system/myapp.service:
[Unit]
Description=My Node.js Web Application
Documentation=https://example.com/docs
After=network.target
Wants=postgresql.service
[Service]
Type=exec
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/usr/bin/node /opt/myapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
What each section does
[Unit] describes the service and its ordering:
Description— shown insystemctl statusAfter=network.target— start after networking is upWants=— a soft dependency (start it too, but continue if it fails). UseRequires=for a hard dependency that fails your service if it fails.
[Service] defines how to run it:
Type=exec— systemd considers the service started once the binary executes. Useforkingfor daemons that background themselves,oneshotfor run-once tasks,notifyfor services that signal readiness.User/Group— run as a non-root account (always do this for app services)WorkingDirectory— the directory the process runs inEnvironment— set environment variables; or useEnvironmentFile=to load them from a fileExecStart— the command to start the service (use absolute paths)Restart=on-failureandRestartSec=5— restart on crash, waiting 5 seconds
[Install] controls boot behavior:
WantedBy=multi-user.target— start at the normal multi-user boot stage when the service is enabled
Loading and starting it
After creating or editing the file, reload systemd and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service
Loading environment from a file
Rather than inline Environment= lines, keep config in a separate file:
[Service]
EnvironmentFile=/etc/myapp/env
ExecStart=/usr/bin/node /opt/myapp/server.js
# /etc/myapp/env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://localhost/myapp
This keeps secrets out of the unit file and lets you change config without editing the service.
Hardening a service
systemd can sandbox a service with a few directives. Add these to [Service] to
reduce what a compromised process can do:
[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/myapp/data
NoNewPrivileges— the process can’t gain new privilegesPrivateTmp— gives it a private/tmpProtectSystem=strict— makes most of the filesystem read-onlyReadWritePaths— the exceptions it’s allowed to write to
Overriding a unit without editing it
To change a package-provided unit, don’t edit it directly — create an override:
sudo systemctl edit myapp.service
This opens a drop-in file under
/etc/systemd/system/myapp.service.d/override.conf that layers on top of the
original. Your changes survive package updates.
FAQ
Which Type= should I use?
exec or simple for most foreground programs, forking for traditional
daemons that fork into the background, oneshot for a task that runs and exits,
notify for services that explicitly signal readiness to systemd.
Why run as a non-root user?
If the service is compromised, an attacker is confined to that user’s
permissions. Create a dedicated system user per service and set User=.
How do I run something on a schedule instead of continuously? Use a systemd timer rather than a long-running service — see systemd timers vs cron.
For the commands to control your new service, see the systemctl reference. If it won’t start, see systemd service failed to start.