Files

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.

  1. Gitea → top right profile picture → Settings → Applications.
  2. Manage Access Tokens section: assign a name (e.g. registry-push).
  3. Under Select scopes: set package to Read and Write.
  4. 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 package token 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:

  1. Create a feature branch: git switch -c <area>/<short> (e.g. deployment/sha-tagging, content/pipeline).
  2. Commit there, push the branch: git push -u origin <branch>.
  3. Open a Pull Request against main in Gitea and merge it there.
  4. 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/elternbeirat is 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 :latest is pulled. Only the eb-blazor image changes on a deploy; eb-pocketbase and its pb_data volume stay as they are.

docker compose up does not automatically re-pull a :latest if 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-blazor to the external NPM Docker network; while the domain has not moved, add a temporary ports: mapping (e.g. 5000:8080) to reach the app on the LAN, and swap it for the npm external 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 with package: 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.