Guide
Writing systemd services and timers for your own apps
On almost every modern Linux distribution, systemd starts and supervises long-running programs. A small unit file is all it takes to make your own app start at boot, restart when it crashes and log to the journal, and a matching timer can replace a cron job. This generator writes those files from a form, with sensible defaults and optional security hardening.
The anatomy of a unit file
A service unit has three sections. [Unit] describes it and declares ordering: After=network-online.target together with Wants= makes systemd start the network first. [Service] says how to run the program: its command, user, folder, environment and restart behaviour. [Install] says when to start it once enabled; WantedBy=multi-user.target means at normal boot.
Service types
simple and exec are right for programs that stay in the foreground, which covers Node, Python, Go and Java servers; exec waits until the binary has actually started before reporting success. notify is for programs that tell systemd when they are ready through sd_notify. forking is for old-style daemons that background themselves, and oneshot for commands that run to completion, such as backups. When a timer is generated, the service becomes oneshot automatically.
Restarts and environment
Restart=on-failure restarts the program when it exits with an error or is killed by a signal; always also restarts after a clean exit. RestartSec= waits between attempts so a crashing program does not spin. Each line in the environment box becomes an Environment= entry, quoted where needed, and EnvironmentFile= loads a file of KEY=value lines, which keeps secrets out of the unit file itself.
Hardening
The hardening option adds a set of sandboxing directives. NoNewPrivileges stops the process from gaining rights through setuid programs, PrivateTmp gives it its own /tmp, ProtectSystem=strict makes the whole file system read-only except for paths listed in ReadWritePaths= (the working directory is added), and ProtectHome hides home folders. These settings shrink the damage a compromised app can do, but an app that writes logs or uploads elsewhere will fail until you add those folders to ReadWritePaths=.
Timers instead of cron
A timer unit triggers a service on a calendar schedule. OnCalendar=daily runs at midnight, Mon..Fri 09:00 on weekday mornings and *:0/15 every fifteen minutes. Persistent=true runs a missed job at the next boot if the machine was off at the scheduled time, and RandomizedDelaySec=60 spreads the start by up to a minute so many machines do not hit a server at once. Every run is logged in the journal.
A worked example
With the defaults, the service file starts the Node app with ExecStart=/usr/bin/node /opt/myapp/server.js as user myapp in /opt/myapp, with Restart=on-failure, RestartSec=5, two Environment= lines, EnvironmentFile=/etc/myapp.env, the hardening block including ProtectSystem=strict, and WantedBy=multi-user.target. Ticking “Also create a .timer” switches the service to oneshot and adds a timer with OnCalendar=daily, Persistent=true and WantedBy=timers.target.
Installing and debugging
Copy the files to /etc/systemd/system/, run systemctl daemon-reload, then systemctl enable --now myapp.service (or the .timer for scheduled jobs). systemctl status myapp shows whether it is running and its last log lines, and journalctl -u myapp.service -f follows the log live. Errors such as “Executable path is not absolute” mean ExecStart needs a full path.
Related tools
- Crontab Generator builds the classic alternative to timers.
- Nginx Config Generator puts the service behind a reverse proxy.
- Chmod Calculator sets permissions on the app folder and environment file.
- .env Converter prepares the EnvironmentFile.
Checked against the current version of the systemd Unit Generator on .

