8.3 KiB
Deployment
How a new version of the website ends up on Unraid. Current state: manual — build the image locally, push it to the Gitea registry, pull it on Unraid. Automation via Gitea Actions comes later.
The site runs as a two-container stack (project eb-stack): the Blazor app
(eb-blazor) plus a PocketBase container (eb-pocketbase) that holds the
content. The stack is described by compose.yaml (the production base) with
overlays layered on top.
Running it locally is a separate document. For starting the stack on your own machine and for how the tests run, see
entwicklung.md. This document is only about getting a built image onto Unraid.
Prerequisites (one-time)
Gitea registry token
The push to the registry needs a Gitea access token with package write
permission (package: Read and Write) — not the account password.
- Gitea → top right profile picture → Settings → Applications.
- Manage Access Tokens section: assign a name (e.g.
registry-push). - Under Select scopes: set
packageto Read and Write. - Click Generate Token, copy the string immediately (shown only once).
The token is a secret. Never store it in plain text in Git, chats, screenshots, or tickets. If one does become visible: delete it in Gitea and generate a new one. A
packagetoken allows uploading arbitrary images to the registry.
The Docker login then stores the token locally, so it only has to be entered once.
Way of working: branches, never directly on main
main is the published state — only :latest is built from it, and only
:latest is pulled by Unraid. That is why we never commit directly to main:
- Create a feature branch:
git switch -c <area>/<short>(e.g.deployment/sha-tagging,content/pipeline). - Commit there, push the branch:
git push -u origin <branch>. - Open a Pull Request against
mainin Gitea and merge it there. - Only then build the release image from
main(below).
Dev builds stay local. For trying things out on your own machine, run the
dev stack (entwicklung.md) — it builds the web image from source and pushes
nothing, so a development state can never accidentally land in the registry as
:latest.
Rolling out a new version (manual)
Prerequisite: logged in to the registry once (see below). Then, on main and with a clean working directory:
scripts/release.sh
The script builds the image, tags it with :latest and the short commit SHA
(for rollback), and pushes both. It aborts if you are not on main or have
uncommitted changes, and warns about unpushed commits.
The image path
gitea.anticarnist.de/tom/elternbeiratis lowercase — container registries require that in the path, even though the user (Tom) and the repo (Elternbeirat) are capitalized.
Logging Unraid in to the registry once
The image is private, so Unraid has to log in once before it can pull. The
Compose Manager Plus plugin has no UI field for this — the login runs through
the Unraid terminal (>_ symbol at the top right of the web interface, prompt
root@Cube:~#):
docker login gitea.anticarnist.de
# Username: Tom
# Password: <package token>
The login stays stored; it only has to be repeated when the token changes. Two pitfalls learned from experience:
- Don't confuse it with PowerShell/laptop. The login has to happen in the
Unraid terminal (
root@Cube), not in Windows PowerShell (PS C:\). The laptop needs the login only to push, Unraid to pull. - A wrong username gets stuck. If the login reports "Stored credentials
invalid or expired" and does not ask for the name, first
docker logout gitea.anticarnist.de, then log in again — otherwise a nonsense username gets stored by accident. - Plain-text warning. Docker stores the token unencrypted in
/root/.docker/config.json. On your own server, okay for a start. Cleaner later: set up a credential helper (→ open item below).
Pull and start again on Unraid
In the Compose Manager Plus plugin (Unraid web interface):
- Docker tab → Compose section → stack eb-stack →
Compose Down, then Compose Up (or "Pull" + "Up", depending on the plugin
version), so that the new
:latestis pulled. Only theeb-blazorimage changes on a deploy;eb-pocketbaseand itspb_datavolume stay as they are.
docker compose updoes not automatically re-pull a:latestif an image of the same name is already present locally. When in doubt, explicitly "Pull" first.
The compose files
compose.yaml is the production base and is the file Unraid uses. It defines
both services with no host port mappings — in production NPM is the only path to
the web app, and the editors reach PocketBase through an NPM subdomain (see #9).
eb-pocketbase keeps its data on the bind mount
/mnt/user/appdata/pocketbase/pb_data, which only exists on the server.
The two overlays (compose.dev.yaml, compose.test.yaml) are for local
development and the tests and are not used on Unraid — see entwicklung.md.
NPM network (still open). The base does not yet attach
eb-blazorto the external NPM Docker network; while the domain has not moved, add a temporaryports:mapping (e.g.5000:8080) to reach the app on the LAN, and swap it for thenpmexternal network once NPM sits in front. Tracked with #9.
Outage: PocketBase unreachable
If eb-pocketbase is down while eb-blazor runs, the app does not render
half-empty pages (the menu comes from PocketBase too). A gate in Program.cs
answers every page request with the static wwwroot/maintenance.html:
| Request | Answer while PocketBase is down |
|---|---|
Any page (/, /board, /posts/…, unknown slugs) |
503, Retry-After: 60, maintenance page |
/health |
503 PocketBase unreachable (for Uptime Kuma) |
/events.ics |
200 with an empty calendar, so subscriptions keep working |
/events/{id}.ics |
503 |
Static files (CSS, fonts, PDFs, /maintenance.html) |
served as usual |
The gate caches the health check for 10 seconds (PocketBaseHealthCache). So
after PocketBase stops, pages can render for up to 10 s more (the components
degrade on their own in that window: empty menu, a short note, 503), and after it
starts again the maintenance page can stay up for up to 10 s. No restart of the
web app is needed — the site comes back by itself.
maintenance.html carries its styles inline, as a copy of the tokens from
app.css. When colours change there, copy them over.
Rollback
scripts/release.sh additionally tags each release with the short commit SHA,
which — unlike the moving :latest — never shifts. To roll back, replace
:latest in the Unraid compose.yaml with the :<sha> of the last working state
and bring it back up:
image: gitea.anticarnist.de/tom/elternbeirat:99dd052 # instead of :latest
Which SHA tags are in the registry is shown by Gitea under
Tom/-/packages → elternbeirat.
Automation
Build and push (done)
.gitea/workflows/deploy.yml builds the image on every push to main and pushes
it to the registry, tagged :latest and :<short-sha> — the same scheme as
scripts/release.sh, just run by the act_runner instead of by hand. It does
not deploy; pulling and starting the new :latest on Unraid stays a manual,
deliberate step (see "Pull and start again on Unraid" above).
Requires two repository secrets (Gitea → repo → Settings → Actions → Secrets):
REGISTRY_USER— the registry username (e.g.Tom).REGISTRY_TOKEN— a Gitea access token withpackage: write, not the login password.
The workflow currently only builds and pushes — it does not run the tests yet, and it does not deploy. Those open steps (tests in the workflow, auto-deploy with a health gate via Watchtower, encrypting the registry token on Unraid) are tracked as Gitea issues under the "Elternbeirat-Website" milestone.
Two known pitfalls: the act_runner needs Docker socket access to build, and it
must offer the ubuntu-latest label the workflow asks for. To check the runner:
Gitea → repo (or site admin) → Settings → Actions → Runners — it
should be listed as online with a label set that includes ubuntu-latest.