Files
pamietnik/infra
Christoph K. ad65102fdc
All checks were successful
Deploy to NAS / deploy (push) Successful in 44s
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 <noreply@anthropic.com>
2026-04-07 18:46:55 +02:00
..

Infrastruktur & Deployment

Architektur

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/<projekt>/
    ├── <app>-Container          :<port> — Anwendung
    ├── uploads/                         — Persistente Uploads
    └── .env                             — Projekt-Secrets

Datenbankverbindung aus Containern: host-gateway:5433


1. Shared PostgreSQL einrichten (einmalig)

sudo mkdir -p /volume2/docker/shared/pgdata
sudo cp infra/docker-compose.yml /volume2/docker/shared/
echo "POSTGRES_PASSWORD=<sicheres-passwort>" | sudo tee /volume2/docker/shared/.env
cd /volume2/docker/shared && sudo docker compose up -d

Datenbank anlegen:

sudo docker exec -it shared-postgres-1 psql -U postgres
CREATE DATABASE <dbname>;
CREATE USER <dbuser> WITH PASSWORD '<passwort>';
GRANT ALL PRIVILEGES ON DATABASE <dbname> TO <dbuser>;
GRANT ALL ON SCHEMA public TO <dbuser>;   -- wichtig für PostgreSQL 15+
\q

2. act_runner einrichten (einmalig)

Token holen: Gitea → Site-Administration → Actions → Runner → Runner erstellen

sudo docker run -d \
  --name gitea-runner \
  --restart unless-stopped \
  --network host \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /volume2/docker/gitea-runner:/data \
  -e GITEA_INSTANCE_URL=http://192.168.1.4:3000 \
  -e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
  -e GITEA_RUNNER_NAME=nas-runner \
  -e GITEA_RUNNER_LABELS=self-hosted,linux,amd64 \
  gitea/act_runner:latest

Wichtig: GITEA_INSTANCE_URL muss die NAS-IP sein, nicht localhost — Job-Container können localhost nicht auflösen.

/volume2/docker/gitea-runner/config.yaml anlegen:

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

sudo mkdir -p /volume2/docker/<projekt>/uploads

3.2 docker-compose.yml (im Repo)

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/<projekt>/uploads:/uploads
    restart: unless-stopped

3.3 Gitea Secrets & Variables

Secrets (Repository → Einstellungen → Actions → Secrets):

Secret Wert
DB_PASSWORD Datenbankpasswort

Variables (Repository → Einstellungen → Actions → Variables):

Variable Wert
DEPLOY_DIR /volume2/docker/<projekt>
DB_USER <dbuser>
DB_NAME <dbname>
APP_PORT <port>

3.4 Gitea Actions Workflow (.gitea/workflows/deploy.yml)

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/<org>/<repo>.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

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

# Logs
sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f postgres
sudo docker compose -f /volume2/docker/<projekt>/docker-compose.yml logs -f

# Backup
sudo docker exec shared-postgres-1 pg_dump -U postgres <dbname> \
  > /volume2/docker/shared/backup_$(date +%Y%m%d)_<dbname>.sql

# Alle Container stoppen
sudo docker compose -f /volume2/docker/shared/docker-compose.yml down
sudo docker compose -f /volume2/docker/<projekt>/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