Create and manage systemd services
A service is a program that runs in the background, such as a web app or a worker. systemd is the tool Ubuntu uses to start, stop, and restart services, and to bring them back after a reboot or a crash. In this guide you will write a small service unit file, start it, and manage it with systemctl.
Before you begin
- A VPS running Ubuntu 24.04 LTS with SSH access as a user with sudo rights, such as alex.
- A program you want to run as a service. This guide uses a tiny Python web app at /opt/hello/app.py as a stand-in.
- Python 3, which Ubuntu installs by default.
Step 1: Create a sample app and a service user
Good practice is to run services under their own unprivileged account, not as root. The --system flag creates a system account, --no-create-home skips a home folder, and nologin stops anyone from logging in as it.
sudo useradd --system --no-create-home --shell /usr/sbin/nologin hello
sudo mkdir -p /opt/hello
sudo nano /opt/hello/app.py
Paste this small app, which answers on port 8080 and logs each request:
from http.server import HTTPServer, BaseHTTPRequestHandler
class Hello(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"Hello from my service\n")
HTTPServer(("127.0.0.1", 8080), Hello).serve_forever()
Hand the folder to the service user, so the app can read its files:
sudo chown -R hello:hello /opt/hello
Step 2: Write the unit file
A unit file describes the service. It lives in /etc/systemd/system/, and the name ends in .service. Create it with nano:
sudo nano /etc/systemd/system/hello.service
[Unit]
Description=Hello example web app
After=network.target
[Service]
User=hello
WorkingDirectory=/opt/hello
ExecStart=/usr/bin/python3 /opt/hello/app.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Here is what the sections mean. [Unit] describes the service and its ordering. [Service] says who runs it, where, and with which command. Restart=on-failure brings it back if it crashes, after waiting five seconds. [Install] says the service should start in normal multi-user boot.
Step 3: Load, enable, and start the service
Tell systemd to re-read its unit files. Then enable the service so it starts at boot, and start it now. --now does both start and enable steps at once.
sudo systemctl daemon-reload
sudo systemctl enable --now hello.service
Check that it is running:
systemctl status hello.service --no-pager
● hello.service - Hello example web app
Loaded: loaded (/etc/systemd/system/hello.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-10-31 09:30:12 UTC; 4s ago
Main PID: 2210 (python3)
Test it with curl. The app listens only on 127.0.0.1, so the request must come from the server itself:
curl http://127.0.0.1:8080/
Hello from my service
Step 4: Read the logs
Everything the service prints goes to the systemd journal. The -u flag selects one unit, and -f follows new lines as they arrive, like tail -f. Press Ctrl+C to stop following.
journalctl -u hello.service -f
Step 5: Restart, stop, and disable
Use these commands for day-to-day control:
sudo systemctl restart hello.service # stop and start again
sudo systemctl stop hello.service # stop until started again
sudo systemctl start hello.service # start a stopped service
sudo systemctl disable hello.service # do not start at boot
systemctl is-active hello.service # print "active" or "inactive"
Whenever you edit a unit file, run sudo systemctl daemon-reload before restarting, so systemd picks up the change.
systemd-analyze verify /etc/systemd/system/hello.service after editing a unit file. It warns about typos and bad settings before you restart.systemctl status keeps showing "activating (auto-restart)", read the journal first. Restart loops can use lots of CPU and fill your logs.Troubleshooting
- "Unit hello.service not found". The file is in the wrong folder or has the wrong name. It must be in
/etc/systemd/system/and end in.service. Rundaemon-reloadafter creating it. - Status says "failed" with exit code 1 or 203/EXEC. systemd cannot run the command. Check that the path in
ExecStartexists, that/usr/bin/python3is correct, and that thehellouser can read/opt/hello. - "Address already in use". Another program already uses port 8080. Find it with
sudo ss -tlnpand stop it, or change the port in the app. - Changes do not take effect. You probably forgot
sudo systemctl daemon-reloadafter editing the unit file, or you did not restart the service.
Next steps
Now you can run any program as a managed service. The next step is usually getting your code onto the server automatically, which is covered in the guide on deploying a site with a git push. Keep the same pattern: a dedicated service user, a unit file, and the journal for logs.
Something not working? Chat or email our support team and we will help you get unstuck.
Contact support