add systemd user unit so the dev server starts at boot

This commit is contained in:
Martin Dvořák 2026-09-28 20:50:07 +00:00
commit 174212750b
3 changed files with 78 additions and 3 deletions

View file

@ -112,6 +112,24 @@ those endpoints and `*.map` for any request that isn't a direct localhost connec
Localhost keeps the full dev experience; the LAN and tunnel get a plain, live-reloading
site. To force the raw server instead, use `npm run dev:next` (don't point a tunnel at it).
### Running it as a service (survives reboot)
On this host the dev server runs as a **user** systemd unit, so it comes back after a
reboot without a login (linger is enabled). The unit lives in the repo at
[`deploy/dvorakmortonxd-dev.service`](./deploy/dvorakmortonxd-dev.service):
```bash
mkdir -p ~/.config/systemd/user
cp deploy/dvorakmortonxd-dev.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now dvorakmortonxd-dev.service
loginctl enable-linger "$USER" # once, so it starts at boot
```
Manage it with `systemctl --user status|restart|stop dvorakmortonxd-dev` and follow logs with
`journalctl --user -u dvorakmortonxd-dev -f`. It shares `.next/dev/lock` with a manual
`npm run dev`, so stop the service before starting one by hand.
## 📡 API Routes
| Route | Method | What it does |
@ -153,6 +171,8 @@ src/app/
SlBackground.tsx → animated ASCII train background
scripts/
dev-server.mjs → custom dev server: live updates without the dev-only debug surface
deploy/
dvorakmortonxd-dev.service → systemd user unit so the dev server starts at boot
public/ → fonts, wallpapers, icons, friend pages, /revival-inc game, llms.txt, robots.txt, sitemap.xml
```

View file

@ -249,6 +249,15 @@ list reordering, not free placement. Don't confuse the two.
`localhost:3000` keeps the full dev experience; the LAN/tunnel get a plain,
live-reloading site. Use `npm run dev:next` only if you explicitly want the
raw, unprotected server (never point the tunnel at it).
- The dev server normally runs on this host as a **user** systemd unit
(`~/.config/systemd/user/dvorakmortonxd-dev.service`, kept in-repo at
`deploy/dvorakmortonxd-dev.service`). `Linger=yes` for uid 1000 means it starts
at boot without a login. `systemctl --user restart dvorakmortonxd-dev` reloads
it; logs with `journalctl --user -u dvorakmortonxd-dev -f`. From a bare shell
with no session bus, prefix those with
`XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus`.
The unit pins `HOSTNAME=0.0.0.0` (the script reads that env var) so the tunnel
can reach :3000, and clears a stale `.next/dev/lock` on start.
- `allowedDevOrigins` (`next.config.ts`) still matters: the tunnel host and any
LAN origin must be listed or Next rejects the HMR WebSocket with a 403 (the
client then never hydrates — SSR only, icons stacked at `(0,0)`, dead
@ -299,9 +308,11 @@ list reordering, not free placement. Don't confuse the two.
## Testing notes
- There is **no test suite**. Verification is `tsc --noEmit`, `eslint`, and
`next build`.
- A dev server may already be running on port 3000; check before starting one.
Two dev servers on this repo collide on `.next/dev/lock`, so stop the old one
(and delete the stale lock) before starting the custom one.
- A dev server may already be running on port 3000 — on this host it is normally
the `dvorakmortonxd-dev` user systemd service, so check
`systemctl --user status dvorakmortonxd-dev` first. Two dev servers on this
repo collide on `.next/dev/lock`; stop the service (or the old process)
before starting the custom server by hand.
- Dev-server guard check: `curl -s -o /dev/null -w '%{http_code}' \
http://localhost:3000/__nextjs_server_status` must be `200` (localhost keeps
debug), while the same path through the LAN IP or `beta.dvorakmortonxd.dev`

View file

@ -0,0 +1,44 @@
# User systemd service for the dvorakmortonxd.dev dev server.
#
# Install (no root needed, runs as the current user):
#
# mkdir -p ~/.config/systemd/user
# cp deploy/dvorakmortonxd-dev.service ~/.config/systemd/user/
# systemctl --user daemon-reload
# systemctl --user enable --now dvorakmortonxd-dev.service
# loginctl enable-linger "$USER" # once: start at boot without logging in
#
# Manage:
# systemctl --user status dvorakmortonxd-dev
# systemctl --user restart dvorakmortonxd-dev
# systemctl --user stop dvorakmortonxd-dev
# journalctl --user -u dvorakmortonxd-dev -f
#
# It shares .next/dev/lock with a manual `npm run dev`, so stop the service
# before starting one by hand.
[Unit]
Description=dvorakmortonxd.dev dev server (custom Next.js dev wrapper)
Documentation=file:///home/martin/dvorakmortonxd.dev/README.md
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/martin/dvorakmortonxd.dev
# Bind all interfaces so the Cloudflare tunnel host can reach :3000. Pin
# HOSTNAME explicitly: the dev server reads process.env.HOSTNAME and would
# otherwise try to bind to whatever systemd/the shell exported.
Environment=HOSTNAME=0.0.0.0
Environment=PORT=3000
# A reboot can leave a stale dev lock behind; clear it before starting.
ExecStartPre=/bin/rm -f /home/martin/dvorakmortonxd.dev/.next/dev/lock
ExecStart=/bin/node /home/martin/dvorakmortonxd.dev/scripts/dev-server.mjs
Restart=always
RestartSec=3
# Give Next.js a moment to shut down cleanly.
TimeoutStopSec=15
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target