Reverts the explicit subnets: the pools were freed by deleting five networks, so the network role is back to its single idempotent task - ensure the network exists and Caddy is on it - and the Sites no longer declare a subnet. The deployment readme keeps the note that a new Site needs a config, and therefore a new tag, but no longer asks for a free /24. If the pools fill up again the failure is recognisable: 'all predefined address pools have been fully subnetted', and the choices are deleting stale networks or declaring a subnet per Site.
95 lines
4.0 KiB
Markdown
95 lines
4.0 KiB
Markdown
# The menu deployment
|
|
|
|
Lives here, beside the app it deploys. One run deploys a Site: it starts the
|
|
app's container and points the Site's web address at it. There is no older
|
|
system to keep serving, so there is no staged rollout and nothing to retire —
|
|
deploying an earlier tag is the rollback.
|
|
|
|
**The app's data root is the directory the old static menus were served from**
|
|
(`/var/lib/menu.<site>`), on purpose, so the app inherits the location instead of
|
|
a second one. The app only reads and writes `data/` and `site.env` inside it. If
|
|
you later want the old menu files gone, delete those files — not the directory,
|
|
which now holds the live content.
|
|
|
|
## What lives where
|
|
|
|
| Thing | Where |
|
|
| -------------------------------------- | ---------------------------------------------------------------------- |
|
|
| Name, tagline, icon, categories, menus | on the server under `/var/lib/menu.<site>/data` — the admin edits them |
|
|
| Theme colours | `config/<site>.site.json` in the repo, baked into the image |
|
|
| Admin password hash, session secret | `/var/lib/menu.<site>/site.env` (untracked, mode 600) |
|
|
| Which image tag runs | `-e tag=<tag>` |
|
|
| Which Site is deployed | the inventory entry in `hosts` |
|
|
| The web address it serves | `host:` in `vars/<site>.yml` |
|
|
|
|
## Deploying
|
|
|
|
From the repo root:
|
|
|
|
```sh
|
|
./deploy.sh 2026-09-22 # checks, podman build+push, then deploys
|
|
SKIP_DEPLOY=1 ./deploy.sh 2026-09-22 # build and push only
|
|
```
|
|
|
|
`deploy.sh` runs `make build-production TAG=<tag>`, which builds a multi-arch
|
|
podman manifest (`linux/amd64,linux/arm64`) and pushes `latest` and the tag to
|
|
`registry.tanshu.com/menu-admin`. Then it runs this playbook against the Site
|
|
named by `SITE` (default `hopsngrains`).
|
|
|
|
By hand:
|
|
|
|
```sh
|
|
make build-production TAG=2026-09-22
|
|
cd ansible
|
|
ansible-playbook --inventory=hosts playbook.yml --limit hopsngrains -e tag=2026-09-22
|
|
```
|
|
|
|
Every Site runs on the same box, so `hosts` has one entry per Site pointing at
|
|
the same `ansible_host` with its own `var_file`. `ansible_host` is written as the
|
|
resolvable name rather than an ssh alias, so the inventory works whatever a
|
|
machine's `~/.ssh/config` says. (`ssh` on some machines complains about
|
|
`/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` being owned by `nobody`; that is
|
|
a local file-permission bug whose fix is `sudo chown -R root:root
|
|
/etc/ssh/ssh_config.d`, not something this playbook works around.)
|
|
|
|
Adding a Site is one line in `hosts`, a copy of `vars/hopsngrains.yml`, and a
|
|
`config/<site>.site.json`. The image is shared, so the config has to be in it:
|
|
a new Site needs a new tag.
|
|
|
|
## Checking it landed
|
|
|
|
```sh
|
|
npm run check-site -- https://menu.hopsngrains.com
|
|
```
|
|
|
|
Read-only, exits non-zero on failure. Then from a phone: the landing page, a
|
|
Section page, the page counter, tap-to-zoom, and signing into `/admin` to
|
|
publish something.
|
|
|
|
## The admin password
|
|
|
|
The app starts with the admin disabled while `/var/lib/menu.<site>/site.env`
|
|
is missing, and the playbook says so rather than failing. Create it once per
|
|
Site; run it again with `--force` to reset the password (a fresh session secret
|
|
signs everyone out). There is no minimum password length.
|
|
|
|
```sh
|
|
scp scripts/provision-site-env.sh monoco:/tmp/
|
|
ssh -t monoco bash /tmp/provision-site-env.sh /var/lib/menu.hopsngrains.com
|
|
ssh monoco docker restart menu-app.hopsngrains.com
|
|
```
|
|
|
|
The `-t` matters: the script prompts for the password, and without a terminal on
|
|
the remote side the prompt cannot be read, so it would sit there looking frozen.
|
|
With no image argument it borrows the one the running container uses.
|
|
|
|
## Rolling back
|
|
|
|
Deploy the previous tag:
|
|
|
|
```sh
|
|
./deploy.sh <previous-tag>
|
|
```
|
|
|
|
`docker logs menu-app.<site>` is where a failed container says why.
|