From ad65102fdc1e079f370a4778b714e011608ef080 Mon Sep 17 00:00:00 2001 From: "Christoph K." Date: Tue, 7 Apr 2026 18:46:55 +0200 Subject: [PATCH] Update infra docs with complete working setup - README.md: rewrite with accurate setup steps including all lessons learned - CLAUDE.md: update with working CI/CD patterns and known pitfalls - gitea-actions.md: new file documenting Gitea Actions usage, expressions, act_runner config, and troubleshooting for future projects Co-Authored-By: Claude Sonnet 4.6 --- infra/CLAUDE.md | 207 +++++++++++++++------------------- infra/README.md | 245 +++++++++++++++++++++++------------------ infra/gitea-actions.md | 156 ++++++++++++++++++++++++++ 3 files changed, 382 insertions(+), 226 deletions(-) create mode 100644 infra/gitea-actions.md diff --git a/infra/CLAUDE.md b/infra/CLAUDE.md index b09e172..78b2aa0 100644 --- a/infra/CLAUDE.md +++ b/infra/CLAUDE.md @@ -1,155 +1,124 @@ -# CLAUDE.md — Pamietnik Infrastruktur +# CLAUDE.md — NAS Infrastruktur -Diese Datei ist Kontext für eine dedizierte Infra-Session. Hier ist alles beschrieben, was zum Aufbau und Betrieb der Infrastruktur auf der Synology NAS notwendig ist. +Kontext für Claude Code Sessions zum Thema Deployment und Infrastruktur auf der Synology NAS. --- ## Umgebung -- **NAS:** Synology DiskStation, DSM 7.x -- **Docker-Datenpfad:** `/volume2/docker/` (alle Container-Daten hier ablegen) -- **Docker-Socket:** `/var/run/docker.sock` -- **Gitea:** läuft als Docker-Container auf der NAS, erreichbar unter `http://localhost:3000` -- **SSH-Zugriff:** `ssh jacek@`, sudo-Befehle erforderlich für Docker +- **NAS:** Synology DiskStation, DSM 7.x, IP: `192.168.1.4` +- **SSH:** `ssh jacek@192.168.1.4` — Docker-Befehle erfordern `sudo` +- **Docker-Datenpfad:** `/volume2/docker/` — alle persistenten Daten hier +- **Docker-Binary auf NAS:** `/usr/local/bin/docker` +- **Gitea:** `http://192.168.1.4:3000` (Docker-Container auf NAS) --- -## Architektur +## Laufende Dienste -``` -Synology NAS -├── Gitea (Docker) :3000 — Git-Server + CI/CD -├── act_runner (Docker) — Gitea Actions Runner -│ -├── /volume2/docker/shared/ -│ └── postgres:16-alpine :5433 — Geteilte DB für alle Projekte -│ -└── /volume2/docker/pamietnik/ - └── api (Go + SPA) :9050 — Pamietnik App +| Dienst | Container | Port | Pfad | +|--------|-----------|------|------| +| PostgreSQL | `shared-postgres-1` | 5433 | `/volume2/docker/shared/` | +| act_runner | `gitea-runner` | — | `/volume2/docker/gitea-runner/` | +| Pamietnik | `pamietnik-api-1` | 9050 | `/volume2/docker/pamietnik/` | + +--- + +## Datenbankzugriff + +**Aus Containern:** `host-gateway:5433` +(`host-gateway` ist Docker's eingebauter Alias für den Host — in `extra_hosts` und `docker-compose.yml` deklarieren) + +**Remote (Heimnetz):** `psql -h 192.168.1.4 -p 5433 -U -d ` + +**Direkt auf NAS:** +```bash +sudo docker exec -it shared-postgres-1 psql -U postgres ``` -**Verbindung App → Datenbank:** über `host-gateway:5433` (Docker-interner Alias für den NAS-Host) - ---- - -## Verzeichnisse auf der NAS - -| Pfad | Inhalt | -|------|--------| -| `/volume2/docker/shared/pgdata` | PostgreSQL-Daten (persistent) | -| `/volume2/docker/shared/.env` | Secrets: `POSTGRES_PASSWORD` | -| `/volume2/docker/shared/docker-compose.yml` | Shared Stack (Postgres + pgAdmin) | -| `/volume2/docker/pamietnik/uploads` | Hochgeladene Bilder (persistent) | -| `/volume2/docker/pamietnik/.env` | `DB_PASSWORD`, `APP_PORT` | -| `/volume2/docker/pamietnik/docker-compose.yml` | Wird via CI/CD aus dem Repo kopiert | -| `/volume2/docker/gitea-runner/` | act_runner Konfiguration & Daten | - ---- - -## Shared Stack (`/volume2/docker/shared/docker-compose.yml`) - -```yaml -services: - postgres: - image: postgres:16-alpine - restart: unless-stopped - ports: - - "5433:5432" - environment: - POSTGRES_USER: ${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} - volumes: - - /volume2/docker/shared/pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] - interval: 5s - timeout: 5s - retries: 5 - -``` - ---- - -## Pamietnik Stack (`docker-compose.yml` im Repo-Root) - -```yaml -services: - api: - build: - context: . - dockerfile: Dockerfile - ports: - - "${APP_PORT:-9050}:8080" - extra_hosts: - - "host-gateway:host-gateway" - environment: - DATABASE_URL: postgres://${DB_USER:-pamietnik}:${DB_PASSWORD:?DB_PASSWORD is required}@host-gateway:5433/${DB_NAME:-pamietnik}?sslmode=disable - LISTEN_ADDR: :8080 - UPLOAD_DIR: /uploads - volumes: - - /volume2/docker/pamietnik/uploads:/uploads - restart: unless-stopped +**Wichtig PostgreSQL 15+:** Nach `GRANT ALL PRIVILEGES ON DATABASE` zusätzlich: +```sql +GRANT ALL ON SCHEMA public TO ; ``` --- ## CI/CD: Gitea Actions -**Workflow:** `.gitea/workflows/deploy.yml` — wird bei Push auf `main` ausgeführt. +**Workflow-Datei:** `.gitea/workflows/deploy.yml` +**Trigger:** Push auf `main` -**Runner:** `gitea/act_runner` Container auf der NAS mit `--network host` und Docker-Socket-Mount. +**Runner-Setup:** +- Container: `gitea/act_runner:latest` mit `--network host` +- `GITEA_INSTANCE_URL` muss NAS-IP sein (`192.168.1.4:3000`), **nicht** `localhost` +- `config.yaml` braucht `valid_volumes: [/volume2/docker]` sonst werden Mounts ignoriert -**Gitea Secrets** (Repository → Einstellungen → Actions → Secrets): -- `DB_PASSWORD` — Passwort des `pamietnik` DB-Users -- `DEPLOY_DIR` — `/volume2/docker/pamietnik` +**Job-Container:** `docker:latest` mit `-v /volume2/docker:/volume2/docker` +- Docker CLI ist im Image enthalten +- Socket wird automatisch vom Runner propagiert (nicht nochmal in `options` mounten → Duplicate-Fehler) +- `wget` statt `curl` verwenden (`curl` nicht im Image) -**Gitea Variables** (Repository → Einstellungen → Actions → Variables): -- `DB_USER` — `pamietnik` -- `DB_NAME` — `pamietnik` -- `APP_PORT` — `9050` +**Gitea Konfiguration:** +- Nicht-sensitive Werte (Pfade, Ports, DB-Namen) → **Variables** (`vars.NAME`) +- Passwörter, Tokens → **Secrets** (`secrets.NAME`) +- Variables mit Leerzeichen am Ende → Parsing-Fehler in Expressions --- -## Dienste & URLs +## Deployment-Muster für neue Projekte -| Dienst | URL | -|--------|-----| -| Pamietnik | `http://:9050` | -| Gitea | `http://:3000` | -| PostgreSQL | `:5433` | +```yaml +# docker-compose.yml im Repo +services: + api: + build: . + ports: + - "${APP_PORT:-8080}:8080" + extra_hosts: + - "host-gateway:host-gateway" + environment: + DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@host-gateway:5433/${DB_NAME} + volumes: + - /volume2/docker//uploads:/uploads + restart: unless-stopped +``` ---- - -## Wichtige Befehle - -```bash -# Shared Stack starten -cd /volume2/docker/shared && sudo docker compose up -d - -# Logs -sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f -sudo docker compose -f /volume2/docker/pamietnik/docker-compose.yml logs -f api - -# Datenbank-User anlegen -sudo docker exec -it shared-postgres-1 psql -U postgres - -# Backup -sudo docker exec shared-postgres-1 pg_dump -U postgres pamietnik \ - > /volume2/docker/shared/backup_$(date +%Y%m%d).sql +```yaml +# .gitea/workflows/deploy.yml +jobs: + deploy: + runs-on: self-hosted + container: + image: docker:latest + options: -v /volume2/docker:/volume2/docker + steps: + - name: Pull code + run: | + if [ -d "${{ vars.DEPLOY_DIR }}/.git" ]; then + git -C ${{ vars.DEPLOY_DIR }} pull + else + git clone http://192.168.1.4:3000//.git ${{ vars.DEPLOY_DIR }} + fi + - name: Write .env + run: printf 'DB_PASSWORD=%s\n' '${{ secrets.DB_PASSWORD }}' > ${{ vars.DEPLOY_DIR }}/.env + - name: Build & Deploy + run: docker compose -f ${{ vars.DEPLOY_DIR }}/docker-compose.yml up --build -d + - name: Health check + run: sleep 15 && wget -qO- http://192.168.1.4:${{ vars.APP_PORT }}/healthz || exit 1 ``` --- -## Synology-spezifische Hinweise +## Bekannte Fallstricke -- Docker-Befehle erfordern `sudo` -- Ports unter 1024 können Container nicht binden → `PGADMIN_LISTEN_PORT: 8080` nötig -- pgAdmin-Verzeichnis braucht UID 5050: `sudo chown -R 5050:5050 /volume2/docker/shared/pgadmin` -- Docker-Socket ist unter `/var/run/docker.sock` erreichbar -- Container Manager UI unterstützt keinen Datei-Mount für den Docker-Socket → `docker run` via SSH nutzen +- **`Secure: true` auf Session-Cookies** schlägt fehl bei HTTP → `Secure: false` setzen +- **Ports unter 1024** können Container auf Synology nicht binden → internen Port > 1024 wählen +- **`localhost`** im Job-Container zeigt auf den Container selbst, nicht auf die NAS +- **Volume-Mounts** in Workflow `container.options` werden doppelt gemountet wenn identisch mit Runner-Mount +- **`/volume2/docker`** muss in `valid_volumes` der `config.yaml` stehen sonst wird Mount ignoriert --- ## Vollständige Setup-Anleitung -Siehe `infra/README.md` im Repo. +Siehe `infra/README.md` diff --git a/infra/README.md b/infra/README.md index 24e0777..9298f65 100644 --- a/infra/README.md +++ b/infra/README.md @@ -1,85 +1,48 @@ # Infrastruktur & Deployment -## Übersicht +## Architektur ``` -Synology NAS -├── /volume2/docker/shared/ ← Geteilte Infrastruktur (PostgreSQL + pgAdmin) -│ ├── docker-compose.yml -│ ├── .env -│ ├── pgdata/ ← PostgreSQL-Daten (persistent) -│ └── pgadmin/ ← pgAdmin-Daten (persistent) -│ -└── /volume2/docker/pamietnik/ ← Pamietnik-Deployment - ├── docker-compose.yml ← Kopie aus dem Repo (via CI/CD) - ├── .env - └── uploads/ ← Hochgeladene Bilder (persistent) +Synology NAS (192.168.1.4) +├── Gitea :3000 — Git + CI/CD +├── act_runner — Gitea Actions Runner +├── /volume2/docker/shared/ +│ ├── postgres:16-alpine :5433 — Geteilte DB (alle Projekte) +│ └── pgdata/ — Persistente Daten +└── /volume2/docker// + ├── -Container : — Anwendung + ├── uploads/ — Persistente Uploads + └── .env — Projekt-Secrets ``` +**Datenbankverbindung aus Containern:** `host-gateway:5433` + --- -## 1. Geteilte Infrastruktur einrichten (einmalig) - -### Verzeichnisse anlegen +## 1. Shared PostgreSQL einrichten (einmalig) ```bash sudo mkdir -p /volume2/docker/shared/pgdata +sudo cp infra/docker-compose.yml /volume2/docker/shared/ +echo "POSTGRES_PASSWORD=" | sudo tee /volume2/docker/shared/.env +cd /volume2/docker/shared && sudo docker compose up -d ``` -### docker-compose.yml kopieren - -```bash -sudo cp infra/docker-compose.yml /volume2/docker/shared/docker-compose.yml -``` - -### .env anlegen - -Datei `/volume2/docker/shared/.env`: - -```env -POSTGRES_PASSWORD= -``` - -### Starten - -```bash -cd /volume2/docker/shared -sudo docker compose up -d -``` - -### Datenbank & User anlegen - +Datenbank anlegen: ```bash sudo docker exec -it shared-postgres-1 psql -U postgres ``` - ```sql -CREATE DATABASE pamietnik; -CREATE USER pamietnik WITH PASSWORD 'deinPasswort'; -GRANT ALL PRIVILEGES ON DATABASE pamietnik TO pamietnik; +CREATE DATABASE ; +CREATE USER WITH PASSWORD ''; +GRANT ALL PRIVILEGES ON DATABASE TO ; +GRANT ALL ON SCHEMA public TO ; -- wichtig für PostgreSQL 15+ \q ``` --- -## 2. Pamietnik-Deployment einrichten (einmalig) - -```bash -sudo mkdir -p /volume2/docker/pamietnik/uploads -``` - -Datei `/volume2/docker/pamietnik/.env`: - -```env -DB_PASSWORD= -APP_PORT=9050 -``` - ---- - -## 3. Gitea CI/CD einrichten (einmalig) - -### act_runner starten +## 2. act_runner einrichten (einmalig) Token holen: **Gitea → Site-Administration → Actions → Runner → Runner erstellen** @@ -90,76 +53,144 @@ sudo docker run -d \ --network host \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /volume2/docker/gitea-runner:/data \ - -e GITEA_INSTANCE_URL=http://localhost:3000 \ - -e GITEA_RUNNER_REGISTRATION_TOKEN= \ + -e GITEA_INSTANCE_URL=http://192.168.1.4:3000 \ + -e GITEA_RUNNER_REGISTRATION_TOKEN= \ -e GITEA_RUNNER_NAME=nas-runner \ -e GITEA_RUNNER_LABELS=self-hosted,linux,amd64 \ gitea/act_runner:latest ``` -### Gitea Secrets & Variables setzen +> **Wichtig:** `GITEA_INSTANCE_URL` muss die NAS-IP sein, nicht `localhost` — +> Job-Container können `localhost` nicht auflösen. -**Repository → Einstellungen → Actions → Secrets:** +`/volume2/docker/gitea-runner/config.yaml` anlegen: +```yaml +runner: + name: "nas-runner" +container: + valid_volumes: + - /volume2/docker +``` + +Runner neu starten: `sudo docker restart gitea-runner` + +--- + +## 3. Neues Projekt deployen + +### 3.1 Verzeichnis anlegen +```bash +sudo mkdir -p /volume2/docker//uploads +``` + +### 3.2 docker-compose.yml (im Repo) +```yaml +services: + api: + build: + context: . + dockerfile: Dockerfile + ports: + - "${APP_PORT:-8080}:8080" + extra_hosts: + - "host-gateway:host-gateway" + environment: + DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@host-gateway:5433/${DB_NAME} + volumes: + - /volume2/docker//uploads:/uploads + restart: unless-stopped +``` + +### 3.3 Gitea Secrets & Variables + +**Secrets** (Repository → Einstellungen → Actions → Secrets): | Secret | Wert | |--------|------| -| `DB_PASSWORD` | Passwort des `pamietnik` DB-Users | -| `DEPLOY_DIR` | `/volume2/docker/pamietnik` | - -**Repository → Einstellungen → Actions → Variables:** +| `DB_PASSWORD` | Datenbankpasswort | +**Variables** (Repository → Einstellungen → Actions → Variables): | Variable | Wert | |----------|------| -| `DB_USER` | `pamietnik` | -| `DB_NAME` | `pamietnik` | -| `APP_PORT` | `9050` | +| `DEPLOY_DIR` | `/volume2/docker/` | +| `DB_USER` | `` | +| `DB_NAME` | `` | +| `APP_PORT` | `` | ---- - -## 4. Dienste & URLs - -| Dienst | URL | -|--------|-----| -| Pamietnik App | `http://:9050` | -| PostgreSQL | `psql -h -p 5433 -U pamietnik -d pamietnik` | - ---- - -## 5. Neues Projekt hinzufügen - -```bash -sudo docker exec -it shared-postgres-1 psql -U postgres -``` - -```sql -CREATE DATABASE neuprojekt; -CREATE USER neuprojekt WITH PASSWORD 'passwort'; -GRANT ALL PRIVILEGES ON DATABASE neuprojekt TO neuprojekt; -\q -``` - -In `docker-compose.yml` des neuen Projekts: +### 3.4 Gitea Actions Workflow (`.gitea/workflows/deploy.yml`) ```yaml -extra_hosts: - - "host-gateway:host-gateway" -environment: - DATABASE_URL: postgres://neuprojekt:passwort@host-gateway:5433/neuprojekt +name: Deploy to NAS + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: self-hosted + container: + image: docker:latest + options: -v /volume2/docker:/volume2/docker + + steps: + - name: Pull code + run: | + if [ -d "${{ vars.DEPLOY_DIR }}/.git" ]; then + git -C ${{ vars.DEPLOY_DIR }} pull + else + git clone http://192.168.1.4:3000//.git ${{ vars.DEPLOY_DIR }} + fi + + - name: Write .env + run: printf 'DB_PASSWORD=%s\n' '${{ secrets.DB_PASSWORD }}' > ${{ vars.DEPLOY_DIR }}/.env + + - name: Build & Deploy + run: docker compose -f ${{ vars.DEPLOY_DIR }}/docker-compose.yml up --build -d + + - name: Health check + run: | + sleep 15 + wget -qO- http://192.168.1.4:${{ vars.APP_PORT }}/healthz || exit 1 ``` --- -## 6. Wartung +## 4. Synology-spezifische Hinweise + +| Problem | Lösung | +|---------|--------| +| `permission denied` bei Docker-Befehlen | Immer `sudo docker ...` verwenden | +| Ports < 1024 nicht bindbar | Internen Port > 1024 wählen, z.B. `LISTEN_PORT: 8080` | +| Docker-Socket im Container Manager UI nicht mountbar | `docker run` via SSH ausführen | +| `act_runner` kann `localhost:3000` nicht erreichen | NAS-IP statt `localhost` in `GITEA_INSTANCE_URL` | +| Volume-Mount wird ignoriert | `valid_volumes` in `config.yaml` eintragen | +| Session-Cookies funktionieren nicht | `Secure: false` setzen bei HTTP-Deployment | +| `GRANT ALL PRIVILEGES` reicht nicht | Zusätzlich `GRANT ALL ON SCHEMA public` ausführen (PG 15+) | + +--- + +## 5. Wartung ```bash # Logs -sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f -sudo docker compose -f /volume2/docker/pamietnik/docker-compose.yml logs -f api +sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f postgres +sudo docker compose -f /volume2/docker//docker-compose.yml logs -f # Backup -sudo docker exec shared-postgres-1 pg_dump -U postgres pamietnik \ - > /volume2/docker/shared/backup_$(date +%Y%m%d).sql +sudo docker exec shared-postgres-1 pg_dump -U postgres \ + > /volume2/docker/shared/backup_$(date +%Y%m%d)_.sql -# Stoppen +# Alle Container stoppen sudo docker compose -f /volume2/docker/shared/docker-compose.yml down -sudo docker compose -f /volume2/docker/pamietnik/docker-compose.yml down +sudo docker compose -f /volume2/docker//docker-compose.yml down ``` + +--- + +## 6. Dienste & Ports + +| Dienst | Port | Beschreibung | +|--------|------|--------------| +| Gitea | 3000 | Git-Server + CI/CD | +| PostgreSQL | 5433 | Geteilte DB (intern: 5432) | +| Pamietnik | 9050 | Tagebuch-App | diff --git a/infra/gitea-actions.md b/infra/gitea-actions.md new file mode 100644 index 0000000..4ee1bd5 --- /dev/null +++ b/infra/gitea-actions.md @@ -0,0 +1,156 @@ +# Gitea Actions — Verwendung & Referenz + +## Grundprinzip + +Gitea Actions ist kompatibel mit GitHub Actions Syntax. Workflow-Dateien liegen in `.gitea/workflows/*.yml` im Repository. Bei Push auf `main` wird der Workflow automatisch ausgeführt. + +``` +Push → Gitea → act_runner auf NAS → Job-Container (docker:latest) → Deploy +``` + +--- + +## Workflow-Struktur + +```yaml +name: Deploy to NAS # Anzeigename in Gitea UI + +on: + push: + branches: [main] # Trigger: Push auf main + +jobs: + deploy: # Job-Name + runs-on: self-hosted # Welcher Runner-Typ + container: + image: docker:latest # Job läuft in diesem Container + options: -v /volume2/docker:/volume2/docker # Volume-Mounts + + steps: + - name: Schritt 1 + run: echo "Shell-Befehl" + + - name: Schritt 2 + run: | + echo "Mehrzeiliger" + echo "Shell-Block" +``` + +--- + +## Expressions + +Werte aus Gitea zur Laufzeit einsetzen: + +| Expression | Quelle | Beispiel | +|------------|--------|---------| +| `${{ secrets.NAME }}` | Secrets (maskiert in Logs) | `${{ secrets.DB_PASSWORD }}` | +| `${{ vars.NAME }}` | Variables (sichtbar in Logs) | `${{ vars.DEPLOY_DIR }}` | +| `${{ github.repository }}` | Repo-Metadaten | `christoph/pamietnik` | +| `${{ github.sha }}` | Aktueller Commit-Hash | `abc1234...` | + +**Secrets vs Variables:** +- **Secrets** → Passwörter, Tokens, API-Keys (`secrets.NAME`) +- **Variables** → Pfade, Ports, Namen, alles Nicht-Sensible (`vars.NAME`) + +Setzen unter: **Repository → Einstellungen → Actions → Secrets / Variables** + +--- + +## act_runner auf der NAS + +Der Runner empfängt Jobs von Gitea und führt sie aus. + +### Laufender Container +```bash +sudo docker ps | grep gitea-runner +sudo docker logs gitea-runner -f # Live-Logs +``` + +### Konfiguration +`/volume2/docker/gitea-runner/config.yaml`: +```yaml +runner: + name: "nas-runner" + +container: + valid_volumes: + - /volume2/docker # Erlaubt Volume-Mounts in Job-Containern +``` + +### Wichtige Einschränkungen auf Synology +- `GITEA_INSTANCE_URL` muss NAS-IP sein (`192.168.1.4:3000`), **nicht** `localhost` + → Job-Container sehen `localhost` als sich selbst, nicht als NAS +- Docker-Socket wird automatisch in Job-Container propagiert + → **nicht** nochmals in `container.options` mounten (→ Duplicate-Fehler) +- `valid_volumes` in `config.yaml` pflegen, sonst werden Mounts ignoriert + +--- + +## Job-Container: docker:latest + +Für Deployments wird `docker:latest` als Job-Container verwendet: + +```yaml +container: + image: docker:latest + options: -v /volume2/docker:/volume2/docker +``` + +**Was ist enthalten:** +- Docker CLI (`docker`, `docker compose`) +- `wget`, `sh`, `git` +- **Nicht enthalten:** `curl`, `bash` (nur `sh`) + +**Volume-Mount `-v /volume2/docker:/volume2/docker`:** +Macht den NAS-Dateipfad im Container verfügbar — notwendig damit git clone, .env schreiben und docker compose auf die richtigen Dateien zugreifen. + +--- + +## Secrets & Variables anlegen + +**Gitea UI:** +1. Repository → Einstellungen → Actions +2. "Secrets" für Passwörter/Tokens → `secrets.NAME` +3. "Variables" für Konfiguration → `vars.NAME` + +**Typische Variablen pro Projekt:** + +| Name | Typ | Beispielwert | +|------|-----|-------------| +| `DEPLOY_DIR` | Variable | `/volume2/docker/pamietnik` | +| `APP_PORT` | Variable | `9050` | +| `DB_USER` | Variable | `pamietnik` | +| `DB_NAME` | Variable | `pamietnik` | +| `DB_PASSWORD` | Secret | `` | + +> **Achtung:** Keine Leerzeichen am Ende von Variable-Werten — führt zu Parsing-Fehlern in Expressions. + +--- + +## Workflow-Status überwachen + +**Gitea UI:** Repository → Actions → laufenden/letzten Job anklicken + +**Runner-Logs live:** +```bash +sudo docker logs gitea-runner -f +``` + +**Job neu triggern (leerer Commit):** +```bash +git commit --allow-empty -m "ci: retrigger deploy" && git push +``` + +--- + +## Fehlersuche + +| Fehler | Ursache | Lösung | +|--------|---------|--------| +| `connection refused :3000` | `localhost` statt NAS-IP | `GITEA_INSTANCE_URL=http://192.168.1.4:3000` | +| `Duplicate mount point` | Socket doppelt gemountet | Nur einmal in `container.options` oder nur Runner-Mount | +| `not a valid volume, will be ignored` | `valid_volumes` fehlt | In `config.yaml` eintragen | +| `curl: not found` | nicht in docker:latest | `wget -qO-` verwenden | +| `cannot find node in PATH` | `actions/checkout@v4` braucht Node | Stattdessen `git clone/pull` direkt verwenden | +| Variable leer (`/docker-compose.yml`) | Als Secret statt Variable gesetzt | Unter Variables (nicht Secrets) anlegen |