Skip to content

/commands/systemd/create-systemd-service

Create a systemd service for a script or app (.service file)

The commands on this page have not been executed by our verification runner yet.updated
tool
systemd
versions
systemd 236+
os
linux

To run a script or application as a systemd service, create a unit file such as /etc/systemd/system/myapp.service with a [Service] section whose ExecStart= is the absolute path of the command, run sudo systemctl daemon-reload, then sudo systemctl enable --now myapp. systemd then starts it at boot, restarts it if you set Restart=, and collects its output in the journal (journalctl -u myapp). The program must stay in the foreground; systemd does the backgrounding.

A minimal service

/etc/systemd/system/myapp.service
[Unit]
Description=My app API server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
Environment=PORT=8080
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/server.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
journalctl -u myapp -f

/opt/myapp, the myapp user and the Python command stand for your own application. Create the user first (sudo useradd --system --home /opt/myapp myapp) or remove the User= line to run as root, which is rarely a good idea.

Main settings

SettingSectionMeaning
Description=UnitName shown by systemctl status
After= / Wants=UnitOrder and soft dependency, for example on network-online.target
Type=simpleServiceDefault: the ExecStart process is the service and stays in the foreground
Type=execServiceLike simple, but start fails if the binary cannot be executed (systemd 240+)
Type=oneshotServiceRuns to completion; combine with RemainAfterExit=yes for setup tasks
Type=forkingServiceFor classic daemons that fork into the background; use PIDFile=
ExecStart=ServiceCommand to run; absolute path, no shell syntax
User= / Group=ServiceAccount the process runs as
WorkingDirectory=ServiceCurrent directory of the process
Environment= / EnvironmentFile=ServiceVariables, inline or from a file
Restart=on-failureServiceRestart after a crash or a non-zero exit
WantedBy=multi-user.targetInstallStart at boot in the normal multi-user state when enabled

Pitfalls

  • Relative paths and PATH. systemd does not read your shell profile. Use absolute paths in ExecStart= and set variables with Environment=.
  • Forgetting daemon-reload. After creating or editing a unit, systemd still uses the old copy and prints Warning: The unit file, source configuration file or drop-ins of myapp.service changed on disk. Run 'systemctl daemon-reload' to reload units.
  • Script without a shebang or execute bit. ExecStart=/opt/myapp/run.sh fails with Exec format error or Permission denied (status 203/EXEC). Add #!/bin/bash and chmod +x.
  • Restart loops. With Restart=always and a program that exits at once, systemd stops after five starts within ten seconds with Start request repeated too quickly.
  • Editing packaged units. Use sudo systemctl edit nginx to create a drop-in override instead of changing files under /usr/lib/systemd/system/.

Check the unit file

systemd-analyze verify /etc/systemd/system/myapp.service reports unknown settings and missing executables before you start it. After starting, systemctl status myapp shows active (running), the main PID and the last log lines; if the start fails, journalctl -u myapp -n 50 shows the program's own error.

FAQ

Where do I put my own unit files?

System services you write go in /etc/systemd/system/. /usr/lib/systemd/system/ (or /lib/systemd/system/) belongs to packages and is overwritten by updates. User services go in ~/.config/systemd/user/ and are managed with systemctl --user.

Can ExecStart use shell syntax like pipes or &&?

No. systemd runs the command directly, without a shell, so pipes, redirections and && are passed as literal arguments. Wrap it, ExecStart=/bin/sh -c 'cmd1 && cmd2', or move the logic into a script.

Why does my service stop right after it starts?

With Type=simple systemd expects the process to keep running. A script that starts a program in the background and exits makes systemd think the service ended, and it stops the rest. Run the program in the foreground, or use Type=forking with PIDFile= for daemons that fork.

Sources