Files
Elternbeirat/docs/deployment.md
T

183 lines
6.2 KiB
Markdown

# 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:
```bash
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:
```bash
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:~#`):
```bash
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**:
```yaml
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:
```yaml
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:
```yaml
image: gitea.anticarnist.de/tom/elternbeirat:99dd052 # statt :latest
```
Which SHA tags are in the registry is shown by Gitea under
`Tom/-/packages` → `elternbeirat`.
---
## Open automation (later)
- `.gitea/workflows/deploy.yml`: build and push on push to `main`.
- Two known pitfalls: the `act_runner` needs Docker socket access; the registry
needs the `write:package` token, not the login password.
- Redeploy via Watchtower **label-scoped**, otherwise it updates the entire
home-lab inventory.
- **Encrypt the registry token on Unraid:** currently it sits in plain text in
`/root/.docker/config.json`. Set up a credential helper later, so that the
plain-text warning from `docker login` disappears.