server/docs/setup/produksjon.md

292 lines
9 KiB
Markdown

# 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=<generer med: openssl rand -hex 32>
POSTGRES_DB=sidelinja
# === Authentik ===
AUTHENTIK_SECRET_KEY=<generer med: openssl rand -hex 64>
AUTHENTIK_POSTGRESQL_PASSWORD=<generer med: openssl rand -hex 32>
# 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=<generer med: openssl rand -hex 32>
# === LiveKit ===
LIVEKIT_API_KEY=<generer>
LIVEKIT_API_SECRET=<generer med: openssl rand -hex 32>
# === OpenRouter ===
OPENROUTER_API_KEY=<fra openrouter.ai>
# === 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 '<AUTHENTIK_POSTGRESQL_PASSWORD>';
CREATE DATABASE authentik OWNER authentik;
CREATE USER forgejo WITH PASSWORD '<FORGEJO_DB_PASSWD>';
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/<slug>/.well-known/openid-configuration`
- Etter oppsett: opprett organisasjon `sidelinja`, opprett repo `sidelinja`
## 11. Backup-strategi
```bash
# Daglig snapshot (cron, 03:00)
# Inkluderer: data/, media/, config/, docker-compose.yml
# Ekskluderer: logs/ (arkiveres separat månedlig)
# Eksempel med restic eller borgbackup:
# borg create /backup/sidelinja::{now} /srv/sidelinja --exclude /srv/sidelinja/logs
```
## 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@<server-ip>
cd /srv/sidelinja
git pull
docker compose build --no-cache <tjeneste>
docker compose up -d <tjeneste>
```
## 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 (når implementert)
- [ ] `https://sidelinja.org` laster SvelteKit-appen
- [ ] `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`