Deploy in one run, with nothing older to preserve
There is no previous system kept serving, so the playbook no longer knows about one: the retire flags, the temporary web address, the task that removed the old nginx snippet and the task that stopped the old container are gone. One run starts the container and points the Site's web address at it, and deploying an earlier tag is the rollback. The vars lose vhost, retire_nginx_snippet, retire_nginx_container and legacy_container; the Caddy role keeps only the snippet upsert. The deployment readme drops its staged-rollout sections and the glossary of words that no longer exist, and the plan's sequence and the operating notes describe the single run instead.
This commit is contained in:
1 parent
041b8b62fe
commit
f2d8058456
8 files changed
+152
-131
No files matched your search
+27
-73
@@ -1,44 +1,20 @@
|
||||
# The menu deployment
|
||||
|
||||
Lives here, beside the app it deploys, the way the other apps on this machine do.
|
||||
It owns the `menu-app` container and the Site's snippet in the shared Caddyfile.
|
||||
|
||||
## Words used below
|
||||
|
||||
The deployment reads as jargon if you have not done one before, so:
|
||||
|
||||
- **web address** — one hostname the web server answers for. Caddy keeps a block
|
||||
per address; `menu.hopsngrains.com` is one. Older writing calls this a
|
||||
_vhost_, which just means "virtual host", and `vhost` is the variable name in
|
||||
`vars/`.
|
||||
- **parallel run** — putting the app on a second, temporary address
|
||||
(`menu-new.hopsngrains.com`) while the old site keeps the real one, so the new
|
||||
one can be checked on a phone without customers seeing it. It needs a DNS
|
||||
record for the temporary name, because Caddy has to fetch a certificate for it.
|
||||
- **cutover** — the moment the real address is pointed at the new app instead of
|
||||
the old nginx container, and that container is stopped. Customers are on the
|
||||
new app from then on. It is reversible.
|
||||
|
||||
| Stage | Address customers use | Address you check |
|
||||
| ------------- | ---------------------------------- | ------------------------------------- |
|
||||
| today | `menu.hopsngrains.com` → old nginx | — |
|
||||
| parallel run | unchanged | `menu-new.hopsngrains.com` → this app |
|
||||
| after cutover | `menu.hopsngrains.com` → this app | — |
|
||||
|
||||
Skipping the parallel run is a legitimate choice: cutover on its own is one
|
||||
command, one Caddy reload, and a rollback if anything looks wrong. The parallel
|
||||
run is for finding out that a container will not start, or a font will not load,
|
||||
without anyone watching.
|
||||
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.
|
||||
|
||||
## What lives where
|
||||
|
||||
| Thing | Where |
|
||||
| -------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| Name, tagline, icon, categories, menus | on the server, under `/var/lib/menu-app.<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-app.<site>/site.env` (untracked, mode 600) |
|
||||
| Which image tag runs | `-e tag=<tag>` |
|
||||
| Which Site is being deployed | the inventory entry in `hosts` |
|
||||
| Thing | Where |
|
||||
| -------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Name, tagline, icon, categories, menus | on the server under `/var/lib/menu-app.<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-app.<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
|
||||
|
||||
@@ -66,50 +42,27 @@ 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 with the fix `sudo chown -R root:root
|
||||
`/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.)
|
||||
|
||||
## The first run, and cutover
|
||||
Adding a Site is one line in `hosts` and a copy of `vars/hopsngrains.yml`.
|
||||
|
||||
The first run targets a temporary vhost, so the live site keeps serving:
|
||||
## Checking it landed
|
||||
|
||||
```sh
|
||||
ansible-playbook --inventory=hosts playbook.yml --limit hopsngrains \
|
||||
-e tag=2026-09-22 -e vhost=menu-new.hopsngrains.com
|
||||
npm run check-site -- https://menu.hopsngrains.com
|
||||
```
|
||||
|
||||
`menu-new.hopsngrains.com` needs a DNS A record pointing at the same address as
|
||||
the live site before this runs: Caddy fetches a certificate for the name, which
|
||||
needs it to resolve publicly.
|
||||
|
||||
Then, from the outside (read-only, exits non-zero on failure):
|
||||
|
||||
```sh
|
||||
npm run check-site -- https://menu-new.hopsngrains.com
|
||||
```
|
||||
|
||||
And from a phone on restaurant wifi: the landing page, a Section page, the page
|
||||
counter, tap-to-zoom, and signing into `/admin` to publish something.
|
||||
|
||||
Cutover is the same playbook with the real vhost and the two retire flags:
|
||||
|
||||
```sh
|
||||
ansible-playbook --inventory=hosts playbook.yml --limit hopsngrains \
|
||||
-e tag=2026-09-22 -e vhost=menu.hopsngrains.com \
|
||||
-e retire_nginx_snippet=true -e retire_nginx_container=true
|
||||
```
|
||||
|
||||
That repoints the real vhost at the app, removes the snippet the old `nginx-menu`
|
||||
playbook wrote for that hostname, and stops the old container. The old container
|
||||
and `/var/lib/menu.hopsngrains.com` are kept, not deleted: deleting 319 MB of the
|
||||
only copy of the old menus is a human decision, made once the new site has been
|
||||
trusted.
|
||||
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 when `/var/lib/menu-app.<site>/site.env`
|
||||
is missing, and the playbook says so. Create it once per Site:
|
||||
The app starts with the admin disabled while `/var/lib/menu-app.<site>/site.env`
|
||||
is missing, and the playbook says so rather than failing. Create it once per
|
||||
Site:
|
||||
|
||||
```sh
|
||||
scp scripts/provision-site-env.sh monoco:/tmp/
|
||||
@@ -119,9 +72,10 @@ ssh monoco docker restart menu-app.hopsngrains.com
|
||||
|
||||
## Rolling back
|
||||
|
||||
Deploy the previous tag:
|
||||
|
||||
```sh
|
||||
ssh monoco docker start nginx-menu.hopsngrains.com
|
||||
./deploy.sh <previous-tag>
|
||||
```
|
||||
|
||||
then point Caddy back at it — the cutover flags above with
|
||||
`retire_nginx_snippet=false`.
|
||||
`docker logs menu-app.<site>` is where a failed container says why.
|
||||
Reference in new issue
Block a user