systemd Unit File Validator

Paste a .service, .timer, .socket, or .mount file and check it for syntax errors, missing directives, and common mistakes.

Client-side only — nothing leaves your browser

Load example:

Paste a systemd unit file on the left to see validation results.

What this validator checks

The validator parses your unit file as INI and runs a series of structural and semantic checks against the rules described in systemd.unit(5), systemd.service(5), systemd.timer(5), systemd.socket(5), and systemd.mount(5). Every check runs locally in your browser — your unit file never leaves the page.

  • Detects the unit type (.service, .timer, .socket, .mount) from section headers and directives
  • Flags lines without an = sign and directives outside any section
  • Verifies required sections per type (e.g. [Service], [Timer], [Mount])
  • Checks that ExecStart uses an absolute path (systemd does not consult PATH)
  • Validates allowed values for Type, Restart, KillMode, StandardOutput, and StandardError
  • Sanity-checks OnCalendar= expressions for timer units
  • Warns when [Install] is missing or has no WantedBy= — needed for systemctl enable
  • Flags unknown directives and unknown section headers (typos like Restar=always)

Common systemd unit file mistakes

MistakeWhat goes wrong
ExecStart=node app.jsRelative command — fails with status=203/EXEC. Use the absolute path: /usr/bin/node.
Description My AppMissing =. Every directive must be Key=Value.
Restart=sometimesInvalid Restart value. Allowed: no, always, on-success, on-failure, on-abnormal, on-watchdog, on-abort.
No [Install] sectionsystemctl enable is a no-op. Add [Install] with WantedBy=multi-user.target.
Type=forking, no PIDFile=systemd cannot reliably track which child is the main process — service may be marked failed.
OnCalendar=every 5 minWrong syntax. Use *:0/5 or *-*-* *:0/5:00.
Multiple ExecStart=Only allowed for Type=oneshot. Other types accept exactly one ExecStart=.
StandardOutput=jurnalTypo — should be journal. Other valid values: null, inherit, file:/path, append:/path.

Using systemd-analyze verify locally

This tool catches the most common authoring mistakes, but systemd ships its own native validator. Once your unit is in place, you can verify it against the running systemd's full schema:

systemd-analyze verify /etc/systemd/system/myapp.service

systemd-analyze verify loads the unit, resolves its dependencies, and prints any issues without actually starting it. Combine it with systemctl daemon-reload after every edit and systemctl status myapp.service to inspect the live state.

Frequently Asked Questions

Where are systemd unit files stored on Linux?
There are three main locations, in order of precedence: /etc/systemd/system/ (admin-installed units, highest priority), /run/systemd/system/ (runtime, second priority), and /lib/systemd/system/ or /usr/lib/systemd/system/ (package-installed units, lowest priority). User units live under ~/.config/systemd/user/ or /etc/systemd/user/. After dropping a new file in any of these directories, run 'sudo systemctl daemon-reload' so systemd picks up the change.
Do systemd unit files need to be executable?
No. Unit files are configuration files, not scripts — systemd reads them. The recommended permissions are 644 (rw-r--r--) and ownership root:root. The binary or script referenced by ExecStart= does need to be executable (chmod +x), but the .service file itself does not. Setting it executable is harmless but signals confusion about how systemd works.
What is the difference between Type=simple, forking, oneshot, and notify?
Type=simple (the default) means the process started by ExecStart is the main process — systemd considers the unit started as soon as it execs. Type=forking expects the process to fork a child and the parent to exit; you must set PIDFile= so systemd can track the child. Type=oneshot is for short-lived commands and is the only Type that allows multiple ExecStart= lines or zero ExecStart= (often paired with RemainAfterExit=yes). Type=notify expects the process to call sd_notify("READY=1") when it has finished initializing — the most accurate startup signal but requires support in the binary.
Why does my service need an [Install] section with WantedBy?
The [Install] section tells systemctl enable how to wire the unit into the boot process. WantedBy=multi-user.target creates a symlink in /etc/systemd/system/multi-user.target.wants/ so the unit starts when the system reaches that target. Without [Install], 'systemctl enable myapp.service' silently does nothing and your service will not start on boot. The [Install] section is only consulted by enable/disable — it is ignored when you start the unit manually with 'systemctl start'.
What does status=203/EXEC mean when my service fails?
Exit status 203/EXEC means systemd was unable to execute the binary specified in ExecStart=. The four most common causes are: (1) the path is relative — systemd does not search PATH, so 'ExecStart=node server.js' fails but 'ExecStart=/usr/bin/node /srv/app/server.js' works; (2) the file does not exist at that path; (3) the file is not marked executable (chmod +x); (4) the interpreter on the shebang line is missing. Run 'systemctl status myapp.service' and check the journal with 'journalctl -u myapp.service -n 50' for the exact error.

Related Tools

Need to manage SSH connections?

SSH Workbench lets you connect, browse files, and manage servers visually.

Try SSH Workbench Free