Tag non-production tabs, and drop the FOODSTER_ prefix

With dev and prod open side by side in the same browser, the tabs were
indistinguishable. ENV is written into the title of every page unless it says
prod, so "dev · Foodster" picks itself out. The value is used verbatim, so
ENV=staging labels itself too, and prod and production both count as unmarked
so a stray capital cannot tag the real instance.

Environment variables lose their prefix: PASSWORD, DB, ENV, ADDR, HOST, REPO,
TAG. The container namespaces them already, and this matches how the other
services here are configured.

PUID/PGID are the exception rather than UID/GID. UID is read-only in bash, so
a value set in .env would be silently replaced by the invoking shell's own and
compose's user: would ignore what was asked for.

Breaking for a running instance: the deployed .env has to be rewritten in the
same deploy, or the app will refuse to start on an unset PASSWORD.
This commit is contained in:
Esa Kataja
2026-09-05 23:55:25 +03:00
parent b8a46cdd30
commit c6a8532569
9 changed files with 124 additions and 51 deletions
+13 -9
View File
@@ -351,7 +351,7 @@ build and no asset bundler.
is imported because the runtime image carries no zoneinfo. All date logic
uses that location explicitly and never `time.Local`.
- **Auth**: HTTP Basic with one shared household password read from
`FOODSTER_PASSWORD`; the username is ignored. Compared using
`PASSWORD`; the username is ignored. Compared using
`subtle.ConstantTimeCompare` over SHA-256 digests so neither the value nor
its length leaks through timing. `/healthz` is the only route outside auth.
- **Exposure**: the app is served on a public hostname behind Traefik, which
@@ -395,21 +395,25 @@ on the server and run with Docker Compose.
`/data/foodster.db`, bind-mounted from `./data` on the host rather than
kept in a named volume, so the file can be listed and copied without going
through the container engine. Backup is `cp -r data`. Because the image
runs as UID 65534, compose sets `user:` from `FOODSTER_UID`/`FOODSTER_GID`
runs as UID 65534, compose sets `user:` from `PUID`/`PGID`
to match whoever owns that directory. `restart: unless-stopped`.
- **Configuration**, entirely through environment variables (see
`.env.example`):
- `FOODSTER_REPO` and `FOODSTER_TAG` — image coordinates.
- `FOODSTER_PASSWORD` — the shared password. Required; the app refuses to
start without it.
- `FOODSTER_DB` — database file path, default `./data/foodster.db`. The
directory is created on startup if missing.
- `FOODSTER_UID` / `FOODSTER_GID` — host owner of `./data`.
Names carry no application prefix: the container namespaces them already.
- `REPO` and `TAG` — image coordinates.
- `PASSWORD` — the shared password. Required; the app refuses to start
without it.
- `DB` — database file path, default `./data/foodster.db`. The directory is
created on startup if missing.
- `ENV` — anything but `prod` is prefixed to the browser tab title, so a
dev instance open beside the real one can be told apart.
- `PUID` / `PGID` — host owner of `./data`. Not `UID`, which is read-only
in bash and would be replaced by the invoking shell's own value.
- `TZ` — default `Europe/Helsinki`.
- The registry hostname exists only in `.env`, which is gitignored, because
§11 leaves open the possibility of publishing this repository.
- **Routing**: Traefik on an external `traefik` network, matching on
`FOODSTER_HOST` and terminating TLS. The container publishes no ports —
`HOST` and terminating TLS. The container publishes no ports —
doing so would put an unencrypted copy of the app on the host, bypassing
the proxy. The hostname lives in `.env` rather than `compose.yaml`, so no
infrastructure detail is committed.