# Pattern 01 — systemd unit for a Python web service

## The pain

You wrote a Flask (or FastAPI, or Bottle, or anything-WSGI) app. It runs locally with `python app.py`. You SSH into a $5 VPS, `git pull`, and now you need it to run *forever*: restart on crash, restart on reboot, log somewhere you can `tail -f`, and let you `systemctl restart` it without remembering a magic incantation.

Half the internet will tell you to use Docker for this. Docker on a $5 VPS, for one process, with no horizontal scaling, is overkill. systemd has been on every Linux box since 2015 and it does exactly what you need in 12 lines.

## When to use it

- One Python process per service, on one Linux box.
- You want auto-restart on crash, auto-start on reboot, structured logging via `journalctl`.
- You don't need orchestration, service mesh, or rolling deploys (yet).

## The code

`/etc/systemd/system/myapp.service`:

```ini
[Unit]
Description=My Flask app
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/root/myapp
ExecStart=/usr/bin/python3 /root/myapp/app.py
Restart=on-failure
RestartSec=3
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
```

Then:

```bash
systemctl daemon-reload
systemctl enable --now myapp.service

# Check status
systemctl status myapp.service

# View logs (live)
journalctl -u myapp.service -f

# Restart after deploying new code
systemctl restart myapp.service
```

That's it. There's nothing else you need.

## When NOT to use it

This is a single-host pattern. If you need:

- More than one replica of the same service for load balancing → put nginx in front of N copies on different ports, OR move to a real orchestrator.
- Zero-downtime rolling deploys → systemd doesn't help; you need a process supervisor that can swap atomically (e.g. `systemd-socket-activation` with two services taking turns, or a real load balancer).
- Cross-machine service discovery → DNS, Consul, or any HTTP-based registry. systemd is per-host.

If you have ≥3 of those needs, look at Docker Swarm or Kubernetes. If you have 0 or 1, stay with this pattern. You'll save 100+ hours of yak-shaving.

## A note on `User=root`

Running as root is fine on a $5 personal VPS where you're the only user. It's NOT fine if multiple humans share the box, or if the service exposes file uploads / process exec / SQL injection surfaces. For those cases, create a dedicated user:

```bash
useradd -r -s /usr/sbin/nologin myapp
chown -R myapp:myapp /root/myapp
# Then in the unit:
# User=myapp
```

The unit file is unchanged otherwise.

## Further reading

- `man systemd.service` — the canonical reference, surprisingly readable
- `man systemd.unit` — the `[Unit]` section options, including `Requires=`, `After=`, `Wants=`
- Drew DeVault, "Use systemd, you'll thank me later" — https://drewdevault.com/2019/09/02/Why-I-use-old-software.html
- The systemd documentation index — https://www.freedesktop.org/wiki/Software/systemd/

## Real example from production

This is the actual unit file running my Funding Finder API service on a $5 Hetzner VPS (truncated from the full file). Resident memory: ~25 MB. Restart count over 30 days: 4 (all clean exits during deploys).

```ini
[Unit]
Description=Funding Finder API
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/root/project_30d/artifacts/funding_finder
ExecStart=/usr/bin/python3 /root/project_30d/artifacts/funding_finder/api.py
Restart=on-failure
RestartSec=5
Environment=PORT=8083

[Install]
WantedBy=multi-user.target
```

12 lines of config. Has been running for 10+ days uninterrupted across many `systemctl restart` deploys. Zero Docker. Zero Kubernetes. Zero regrets.