# Oppsett: Produksjonsserver (Hetzner VPS) **Filsti:** `docs/setup/produksjon.md` Denne oppskriften tar en fersk Ubuntu VPS fra null til en komplett Sidelinja-installasjon. Hvert steg er sekvensielt — ikke hopp over noe. ## 0. Forutsetninger - Hetzner VPS med Ubuntu 24.04 LTS (8 vCPU, 16 GB RAM minimum) - DNS A-records som peker til VPS-ens IP: - `sidelinja.org` + `*.sidelinja.org` - `vegard.info` + `*.vegard.info` - SSH-tilgang med nøkkelpar (passordautentisering deaktiveres i steg 1) ## 1. Grunnsikring av VPS ```bash # Oppdater systemet apt update && apt upgrade -y # Opprett tjenestebruker (ikke kjør alt som root) adduser sidelinja usermod -aG sudo sidelinja # Kopier SSH-nøkkel til ny bruker mkdir -p /home/sidelinja/.ssh cp ~/.ssh/authorized_keys /home/sidelinja/.ssh/ chown -R sidelinja:sidelinja /home/sidelinja/.ssh # Deaktiver passordautentisering og root-login sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config systemctl restart sshd # Brannmur: kun SSH, HTTP, HTTPS ufw allow OpenSSH ufw allow 80/tcp ufw allow 443/tcp ufw enable ``` **Logg ut og logg inn som `sidelinja` fra nå av.** ## 2. Installer Docker ```bash # Docker Engine (offisiell repo) sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin # Kjør Docker uten sudo sudo usermod -aG docker sidelinja newgrp docker ``` ## 3. Opprett mappestruktur ```bash sudo mkdir -p /srv/sidelinja/{config,data,media,logs} sudo mkdir -p /srv/sidelinja/config/{caddy,authentik} sudo mkdir -p /srv/sidelinja/data/{postgres,spacetimedb,forgejo,authentik} sudo mkdir -p /srv/sidelinja/media/podcast sudo mkdir -p /srv/sidelinja/logs/caddy sudo chown -R sidelinja:sidelinja /srv/sidelinja ``` Resultat: ``` /srv/sidelinja/ ├── docker-compose.yml ├── .env ├── config/ │ ├── caddy/Caddyfile │ └── authentik/ ├── data/ │ ├── postgres/ │ ├── spacetimedb/ │ ├── forgejo/ │ └── authentik/ ├── media/ │ └── podcast/ └── logs/ └── caddy/ ``` ## 4. Miljøvariabler (.env) ```bash cat > /srv/sidelinja/.env << 'EOF' # === Domener === DOMAIN_SIDELINJA=sidelinja.org DOMAIN_VEGARD=vegard.info DOMAIN_AUTH=auth.sidelinja.org COMPOSE_PROJECT_NAME=sidelinja # === PostgreSQL === POSTGRES_USER=sidelinja POSTGRES_PASSWORD= POSTGRES_DB=sidelinja # === Authentik === AUTHENTIK_SECRET_KEY= AUTHENTIK_POSTGRESQL_PASSWORD= # Authentik bruker sin egen database i samme PostgreSQL-instans AUTHENTIK_POSTGRESQL_HOST=postgres AUTHENTIK_POSTGRESQL_USER=authentik AUTHENTIK_POSTGRESQL_NAME=authentik # === Forgejo === FORGEJO_DB_PASSWD= # === LiveKit === LIVEKIT_API_KEY= LIVEKIT_API_SECRET= # === OpenRouter === OPENROUTER_API_KEY= # === Intern === # Ingen porter eksponeres utenom 80/443. Alt rutes internt via Docker-nettverket. EOF chmod 600 /srv/sidelinja/.env ``` ## 5. Tjeneste-installasjon (rekkefølge) Tjenestene startes i rekkefølge fordi noen avhenger av andre. Alle defineres i `docker-compose.yml`, men vi verifiserer hvert lag før vi går videre. ### Lag A: Fundament (ingen avhengigheter mellom seg) 1. **Docker-nettverk:** Opprett internt nettverk `sidelinja-net` 2. **PostgreSQL:** Start, opprett databaser for Authentik og Forgejo, verifiser (`pg_isready`) 3. **Caddy:** Start med Caddyfile for alle domener, verifiser at HTTPS fungerer 4. **Authentik:** Start, gjennomfør initial setup via `https://auth.sidelinja.org` 5. **Forgejo:** Start med Authentik som OAuth2-provider, opprett organisasjon og repo ### Lag B: Sanntid (krever nettverk) 6. **SpacetimeDB:** Start, verifiser tilkobling 7. **LiveKit:** Start, verifiser at WebRTC fungerer ### Lag C: Applikasjon (krever alt over) 8. **SvelteKit:** Bygg og start container, verifiser at frontenden laster 9. **Rust Workers:** Bygg og start container(e), verifiser at jobbkøen polles ## 6. docker-compose.yml (skjelett) ```yaml # Fullstendig docker-compose.yml bygges ut når tjenestene implementeres. # Denne seksjonen dokumenterer strukturen og viktige regler. # REGLER: # - Ingen "ports:" mot host UTENOM Caddy (80, 443) # - Alle tjenester på samme interne nettverk (sidelinja-net) # - Volumer bruker bind mounts til /srv/sidelinja/ # - .env-filen lastes automatisk av Docker Compose networks: sidelinja-net: driver: bridge services: caddy: # Eneste tjeneste med eksponerte porter (80, 443) postgres: # data:/srv/sidelinja/data/postgres authentik: # SSO for alle domener, på auth.sidelinja.org forgejo: # data:/srv/sidelinja/data/forgejo, på git.sidelinja.org spacetimedb: # data:/srv/sidelinja/data/spacetimedb livekit: # Intern port, proxyet via Caddy sveltekit: # Intern port, proxyet via Caddy workers: # Rust job workers, ingen porter ``` ## 7. Caddy (Caddyfile grunnstruktur) ```caddyfile # === SSO (felles for alle domener) === auth.sidelinja.org { reverse_proxy authentik:9000 } # === Sidelinja (hovedapplikasjon) === sidelinja.org { # SvelteKit (frontend + API) reverse_proxy sveltekit:3000 # LiveKit (WebSocket upgrade) handle_path /livekit/* { reverse_proxy livekit:7880 } # SpacetimeDB (WebSocket) handle_path /spacetime/* { reverse_proxy spacetimedb:3000 } # Podcast media (statiske filer med byte-range support) handle_path /media/* { root * /srv/sidelinja/media file_server } # Podcast access log (kun media-forespørsler) log { output file /srv/sidelinja/logs/caddy/podcast_access.log format json } } # === Forgejo (Git) === git.sidelinja.org { reverse_proxy forgejo:3000 } # === Vegard.info === vegard.info { # Konfigureres når innhold er klart respond "Under construction" 200 } ``` ## 8. PostgreSQL: Initielle databaser Ved første oppstart må det opprettes separate databaser og brukere for Authentik og Forgejo: ```sql -- Kjøres mot PostgreSQL etter første start -- (eller via init-script montert til /docker-entrypoint-initdb.d/) CREATE USER authentik WITH PASSWORD ''; CREATE DATABASE authentik OWNER authentik; CREATE USER forgejo WITH PASSWORD ''; CREATE DATABASE forgejo OWNER forgejo; ``` ## 9. Authentik: Initial konfigurasjon Etter oppstart, gå til `https://auth.sidelinja.org/if/flow/initial-setup/`: 1. Opprett admin-konto 2. Opprett OAuth2/OpenID Connect-provider for Forgejo 3. Opprett OAuth2/OpenID Connect-provider for SvelteKit (senere) 4. Konfigurer brukergrupper etter behov (redaksjon, admin) ## 10. Forgejo: Koble til Authentik Forgejo konfigureres med Authentik som OAuth2-kilde: - Authentication Source: OAuth2 - Provider: OpenID Connect - Discovery URL: `https://auth.sidelinja.org/application/o//.well-known/openid-configuration` - Etter oppsett: opprett organisasjon `sidelinja`, opprett repo `sidelinja` ## 11. Backup-strategi Se `ARCHITECTURE.md` seksjon 2.2 for full dataklassifisering. Kun kategori 1 (kritisk) og Forgejo-data backupes. ### 11.1 PostgreSQL (daglig, 03:00) ```bash # pg_dump er konsistent selv under last — ingen nedetid docker compose exec -T postgres pg_dump -U sidelinja -Fc sidelinja \ > /srv/sidelinja/backup/pg/sidelinja_$(date +%Y%m%d).dump # Behold 30 dager, slett eldre find /srv/sidelinja/backup/pg/ -name "*.dump" -mtime +30 -delete ``` ### 11.2 Media-filer (daglig, 03:30) ```bash # Inkrementell med rsync til lokal backup-disk eller ekstern lagring rsync -a --delete /srv/sidelinja/media/ /srv/sidelinja/backup/media/ ``` ### 11.3 Forgejo-data (daglig, 04:00) ```bash # Forgejo-repos kan gjenskapes, men det er tidkrevende. # Sikkerhetsnett-backup av hele data-mappen: rsync -a --delete /srv/sidelinja/data/forgejo/ /srv/sidelinja/backup/forgejo/ ``` ### 11.4 Hemmeligheter (.env) ```bash # Manuell kopi ved endring — ALDRI i Git cp /srv/sidelinja/.env /srv/sidelinja/backup/env_$(date +%Y%m%d) chmod 600 /srv/sidelinja/backup/env_* ``` ### 11.5 Cron-oppsett ```bash # /etc/cron.d/sidelinja-backup 0 3 * * * sidelinja /srv/sidelinja/scripts/backup-pg.sh 30 3 * * * sidelinja /srv/sidelinja/scripts/backup-media.sh 0 4 * * * sidelinja /srv/sidelinja/scripts/backup-forgejo.sh ``` ### 11.6 Hva som IKKE backupes (bevisst) - **Redis** — cache, regenereres automatisk - **Caddy-data** — sertifikater regenereres av Let's Encrypt - **Avledede data i PG** (ren tekst, segmenter, søkeindeks) — regenereres fra Git - **Logger** — rulleres med logrotate, arkiveres separat ved behov - **Whisper-modeller** — re-download fra HuggingFace - **SpacetimeDB** — sanntidsdata synkes til PG, in-memory state er flyktig ### 11.7 Restore-prosedyre ```bash # 1. PostgreSQL docker compose exec -T postgres pg_restore -U sidelinja -d sidelinja --clean \ < /srv/sidelinja/backup/pg/sidelinja_YYYYMMDD.dump # 2. Media rsync -a /srv/sidelinja/backup/media/ /srv/sidelinja/media/ # 3. Forgejo docker compose down forgejo rsync -a /srv/sidelinja/backup/forgejo/ /srv/sidelinja/data/forgejo/ docker compose up -d forgejo # 4. Avledede data: trigges automatisk ved webhook eller manuelt # Rust-worker reimporterer alle SRT-filer fra Git til PG ``` ## 12. Deploy-workflow (etter initial setup) Etter at serveren er satt opp, er dette den daglige deploy-flyten: ```bash # Fra lokal maskin (WSL2): git push forgejo main # SSH inn til server: ssh sidelinja@ cd /srv/sidelinja git pull docker compose build --no-cache docker compose up -d ``` ## 13. Verifisering etter oppsett ### Lag A (minimum fungerende server) - [ ] `https://auth.sidelinja.org` viser Authentik login - [ ] `https://git.sidelinja.org` viser Forgejo, innlogging via Authentik fungerer - [ ] PostgreSQL: `docker compose exec postgres pg_isready` returnerer OK - [ ] SSH-push fra lokal WSL2 til Forgejo fungerer ### Lag B-C - [x] `https://sidelinja.org` laster SvelteKit-appen (deployet 2025-03-15) - [x] `https://sidelinja.org/api/health` returnerer 200 - [ ] Authentik OIDC-innlogging fungerer fra nettleser (verifiseres manuelt) - [ ] Chat: meldinger sendes og vises med riktig brukernavn - [ ] `https://vegard.info` svarer - [ ] SpacetimeDB: WebSocket-tilkobling fra nettleser fungerer - [ ] LiveKit: Test-rom med video/lyd fungerer - [ ] Media: `curl -I https://sidelinja.org/media/podcast/test.mp3` returnerer `Accept-Ranges: bytes`