Build a service

Load and start a new unit

Validate a unit, reload the manager, start it, and confirm the process and logs before enabling boot startup.

systemd does not watch your unit files. It parses them once and keeps the result in memory. When you create or edit a unit, you have to tell the manager to read it again. Forgetting that step is the most common systemd mistake I know of.

The sequence

Here is the order I follow every time. Verify, reload, start, check:

systemd-analyze verify /etc/systemd/system/demo-api.service
sudo systemctl daemon-reload
sudo systemctl start demo-api.service
systemctl status demo-api.service
journalctl -u demo-api.service -b

systemd-analyze verify catches syntax errors, unknown directives, and missing executables before anything runs. daemon-reload makes the manager reread the unit files. start runs the service once. status plus the journal confirm two things: the main PID exists, and the application itself logged a healthy startup.

That last point matters. active (running) means a process is alive. It does not mean the app is serving requests. Read what the app printed.

Two failures you will hit

If you edit a unit and restart without reloading, systemd runs the old definition and tells you so in systemctl status:

Warning: The unit file, source configuration file or drop-ins of
demo-api.service changed on disk. Run 'systemctl daemon-reload' to reload units.

Read the warnings at the top of the status output. This one means the running service and the file on disk disagree. You are debugging a unit that is not the one running.

The other classic is a wrong WorkingDirectory=. The process never starts, and the journal names the exact step that failed:

demo-api.service: Changing to the requested working directory failed: No such file or directory
demo-api.service: Failed at step CHDIR spawning /usr/bin/node: No such file or directory
demo-api.service: Main process exited, code=exited, status=200/CHDIR

Notice the exit status. Codes in the 200 range come from systemd itself, before your program ran a single line. status=200/CHDIR points at the working directory. status=203/EXEC points at the executable path or its permissions. status=217/USER means the User= account does not exist. When you see one of these, do not debug your application. Debug the unit.

Enable only after it works

Enabling a broken service schedules a failure for the next boot. So start first, confirm, and only then wire it into boot:

sudo systemctl enable demo-api.service
systemctl is-enabled demo-api.service
Created symlink /etc/systemd/system/multi-user.target.wants/demo-api.service → /etc/systemd/system/demo-api.service.
enabled

The first line shows the symlink from the previous lessons. That symlink is the entire “start at boot” mechanism.

You will also see systemctl enable --now, which enables and starts in one command. I use it only for units I have already tested with a plain start.

Try the full sequence with a disposable service. Break it on purpose: point WorkingDirectory= at a folder that does not exist and read the 200/CHDIR line yourself. Then fix it, reload, and start again. Reboot only in a test environment. On a real server, systemctl is-enabled is enough proof of the intended boot state.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →