Files
Elternbeirat/docs/deployment.md
T

7.0 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.

Test vs. production. As long as the domain has not moved yet and NPM is not in front of it, the container runs with a port mapping and is directly reachable on the local network (http://<unraid>:5000). In production the mapping is gone — then NPM is the only path to the container. The two compose variants are described separately below.


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 images stay local. For trying things out on your own machine:

scripts/dev-build.sh      # builds elternbeirat-web:dev-<branch>, pushes NOTHING

This way 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 elternbeirat → Compose Down, then Compose Up (or "Pull" + "Up", depending on the plugin version), so that the new :latest is pulled.

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.


compose variants

Test (now: without NPM, directly reachable on the LAN)

Present in the repo as compose.yaml. Pulls the registry image and maps port 5000 → 8080:

services:
  eb-web:
    image: gitea.anticarnist.de/tom/elternbeirat:latest
    container_name: eb-web
    restart: unless-stopped
    environment:
      ASPNETCORE_URLS: http://+:8080
      TZ: Europe/Berlin
    ports:
      - "5000:8080"   # TEST access, remove in production

Reachable at http://<unraid>:5000.

Production (later: only via NPM)

No ports: block, instead the external NPM Docker network:

services:
  eb-web:
    image: gitea.anticarnist.de/tom/elternbeirat:latest
    container_name: eb-web
    restart: unless-stopped
    environment:
      ASPNETCORE_URLS: http://+:8080
      TZ: Europe/Berlin
    networks: [npm]

networks:
  npm:
    external: true

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   # statt :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 in tasks/002-deployment-automatisieren.md.

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.