From 174212750be997cb1d8338a2433e99a38ac0c944 Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Mon, 28 Sep 2026 20:50:07 +0000 Subject: [PATCH] add systemd user unit so the dev server starts at boot --- README.md | 20 ++++++++++++++ agents.md | 17 +++++++++--- deploy/dvorakmortonxd-dev.service | 44 +++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 deploy/dvorakmortonxd-dev.service diff --git a/README.md b/README.md index 5400baa..382a40f 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/agents.md b/agents.md index 6b9ba4a..e69c303 100644 --- a/agents.md +++ b/agents.md @@ -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` diff --git a/deploy/dvorakmortonxd-dev.service b/deploy/dvorakmortonxd-dev.service new file mode 100644 index 0000000..8164a14 --- /dev/null +++ b/deploy/dvorakmortonxd-dev.service @@ -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