Deployment
Guide för att driftsätta Kunskapsportal i produktion.
Teknisk bakgrund
Kunskapsportal är en multi-container applikation som består av:
Kärnteknologier som behöver driftsättas:
- Next.js 15 (Node.js 20+) - Huvudapplikationen med Payload CMS
- PostgreSQL 15 - Primär databas för allt innehåll
- Qdrant - Vektordatabas för RAG (semantisk sökning)
- LibreOffice - Headless konvertering av Office-dokument
Externa API-tjänster (krävs):
- Google Gemini API - AI för OCR, chatt, metadatagenerering
- OpenAI API - Text embeddings för vektorsökning
- Mistral API (valfritt) - Alternativ OCR-motor
Deployment-metoder:
- Docker Compose (rekommenderas för kommuner)
- Kubernetes (för större skalning)
- Cloud platforms (Vercel, Railway, DigitalOcean)
Innehållsförteckning
Översikt
Kunskapsportal kan driftsättas på flera sätt:
| Metod | Svårighetsgrad | Kostnad | Skalbarhet |
|---|---|---|---|
| Docker Compose | Lätt | Låg | Begränsad |
| Kubernetes | Medel | Medel | Hög |
| Cloud (Vercel/Railway) | Lätt | Medel | Hög |
| VPS (DigitalOcean) | Medel | Låg | Medel |
Rekommendation för kommuner: Docker Compose på dedikerad server
Docker Deployment
Produktion med Docker Compose
Förutsättningar
- Linux-server (Ubuntu 22.04 LTS rekommenderas)
- Docker & Docker Compose installerat
- Domännamn (t.ex. kunskapsportal.kommun.se)
- SSL-certifikat (Let's Encrypt)
Steg 1: Förbered server
# Uppdatera systemet
sudo apt update && sudo apt upgrade -y
# Installera Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Installera Docker Compose
sudo apt install docker-compose-plugin
# Skapa användare
sudo useradd -m -s /bin/bash kunskapsportal
sudo usermod -aG docker kunskapsportal
# Byt till användaren
sudo su - kunskapsportal
Steg 2: Klona och konfigurera
# Klona repositoryt
git clone https://github.com/Falkenbergs-kommun/kunskapsportal.git
cd kunskapsportal
# Kopiera och konfigurera miljövariabler
cp .env.example .env
nano .env
Produktions-.env:
# Databas (GENERERA STARKA LÖSENORD!)
DATABASE_URI=postgres://knowledge_user:MYCKET_STARKT_LÖSENORD@postgres:5432/knowledge_base
DB_PASSWORD=MYCKET_STARKT_LÖSENORD
DB_PUSH=true
# Payload CMS (GENERERA HEMLIG NYCKEL!)
PAYLOAD_SECRET=$(openssl rand -base64 32)
PAYLOAD_URL=https://kunskapsportal.din-kommun.se
# Docker Qdrant
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=
QDRANT_ENABLED=true
# AI-tjänster
GEMINI_API_KEY=din-produktions-nyckel
OPENAI_API_KEY=sk-din-produktions-nyckel
MISTRAL_API_KEY=din-produktions-nyckel
PDF_EXTRACTOR=mistral
# Produktionsinställningar
NODE_ENV=production
NEXT_TELEMETRY_DISABLED=1
NODE_OPTIONS="--max-old-space-size=4096"
Steg 3: Konfigurera reverse proxy (Nginx)
Installera Nginx:
sudo apt install nginx certbot python3-certbot-nginx
Skapa Nginx-konfiguration:
sudo nano /etc/nginx/sites-available/kunskapsportal
server {
server_name kunskapsportal.din-kommun.se;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Max upload size (anpassa efter behov)
client_max_body_size 100M;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts för AI-processing
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# Static files cache
location /_next/static {
proxy_pass http://localhost:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Media files
location /media {
proxy_pass http://localhost:3000;
add_header Cache-Control "public, max-age=86400";
}
}
Aktivera konfiguration:
sudo ln -s /etc/nginx/sites-available/kunskapsportal /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Skaffa SSL-certifikat:
sudo certbot --nginx -d kunskapsportal.din-kommun.se
Steg 4: Starta tjänsterna
# Bygg Docker-imagen
docker-compose build
# Starta alla tjänster
docker-compose up -d
# Följ loggarna
docker-compose logs -f app
Steg 5: Verifiera deployment
# Kontrollera container-status
docker-compose ps
# Testa hälsokontroll
curl https://kunskapsportal.din-kommun.se/api/health
# Kolla loggarna
docker-compose logs --tail=100 app
Autostart med systemd
Skapa systemd-service:
sudo nano /etc/systemd/system/kunskapsportal.service
[Unit]
Description=Kunskapsportal
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/home/kunskapsportal/kunskapsportal
ExecStart=/usr/bin/docker-compose up -d
ExecStop=/usr/bin/docker-compose down
User=kunskapsportal
Group=kunskapsportal
[Install]
WantedBy=multi-user.target
Aktivera autostart:
sudo systemctl daemon-reload
sudo systemctl enable kunskapsportal
sudo systemctl start kunskapsportal
Cloud Providers
Railway
Railway erbjuder enkel deployment med managed PostgreSQL.
Steg:
- Skapa konto på Railway.app
- New Project → Deploy from GitHub
- Välj kunskapsportal-repositoryt
- Add PostgreSQL database
- Konfigurera miljövariabler
- Deploy!
Miljövariabler att sätta:
DATABASE_URI(från Railway PostgreSQL)PAYLOAD_SECRETGEMINI_API_KEYOPENAI_API_KEYQDRANT_URL(Qdrant Cloud)
Kostnad: ~$20/månad för Hobby plan
Vercel (Frontend + API Routes)
Vercel kan hosta Next.js-appen, men kräver extern databas.
Observera: Payload admin fungerar inte fullt ut på Vercel pga. Serverless-begränsningar.
Användningsfall: Frontend + API endast
Deployment:
npm install -g vercel
vercel
DigitalOcean Droplet
1. Skapa Droplet:
- Ubuntu 22.04 LTS
- Minst 4GB RAM
- 2 vCPUs
2. Konfigurera enligt Docker-instruktionerna ovan
3. Konfigurera brandvägg:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Kostnad: ~$24/månad för Basic Droplet
Qdrant Cloud
Istället för att köra Qdrant lokalt, använd Qdrant Cloud.
Fördelar:
- Managed service
- Automatiska backups
- Bättre prestanda
- Enkel skalning
Steg:
- Skapa konto på cloud.qdrant.io
- Skapa cluster
- Notera URL och API-nyckel
- Uppdatera
.env:
QDRANT_URL=https://xyz-abc123.eu-central.aws.cloud.qdrant.io
QDRANT_API_KEY=din-api-nyckel
Kostnad: Gratis tier finns, sedan från $25/månad
Säkerhet
Miljövariabler
ALDRIG committa känslig data:
# Lägg till i .gitignore
.env
.env.local
.env.production
Använd starka lösenord:
# Generera säkra nycklar
openssl rand -base64 32
# Eller
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Firewall
# UFW (Ubuntu)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
Fail2ban
Skydda mot brute-force:
sudo apt install fail2ban
# Konfigurera
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
SSL/TLS
Alltid använd HTTPS i produktion:
# Let's Encrypt (gratis)
sudo certbot --nginx -d kunskapsportal.din-kommun.se
# Auto-renewal
sudo certbot renew --dry-run
Security Headers
Lägg till i Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:;" always;
Databas-säkerhet
# PostgreSQL konfiguration
sudo nano /etc/postgresql/15/main/pg_hba.conf
# Tillåt endast lokala anslutningar
local all all peer
host all all 127.0.0.1/32 scram-sha-256
Backup & Recovery
Automatisk PostgreSQL-backup
Skapa backup-script:
nano ~/backup-db.sh
#!/bin/bash
BACKUP_DIR="/home/kunskapsportal/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/kunskapsportal_$DATE.sql.gz"
# Skapa backup
docker-compose exec -T postgres pg_dump -U knowledge_user knowledge_base | gzip > $BACKUP_FILE
# Behåll endast 30 dagar
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
echo "Backup skapad: $BACKUP_FILE"
chmod +x ~/backup-db.sh
Schemalägg med cron:
crontab -e
# Backup kl 02:00 varje natt
0 2 * * * /home/kunskapsportal/backup-db.sh >> /home/kunskapsportal/backup.log 2>&1
Återställ från backup
# Stoppa applikationen
docker-compose down
# Återställ databas
gunzip < kunskapsportal_20250107_020000.sql.gz | \
docker-compose exec -T postgres psql -U knowledge_user knowledge_base
# Starta applikationen
docker-compose up -d
Qdrant-backup
Lokal Qdrant:
# Backup
docker cp qdrant:/qdrant/storage ./qdrant_backup
# Restore
docker cp ./qdrant_backup qdrant:/qdrant/storage
Qdrant Cloud:
Backups sköts automatiskt av Qdrant Cloud.
Offsite backup
Ladda upp till S3/Backblaze:
# Installera AWS CLI eller rclone
sudo apt install rclone
# Konfigurera
rclone config
# Synka backups
rclone sync ~/backups remote:kunskapsportal-backups
Monitoring
Uptime monitoring
Uptime Robot (Gratis):
- Gå till uptimerobot.com
- Skapa monitor för
https://kunskapsportal.din-kommun.se/api/health - Konfigurera alerter (email/SMS)
Loggning
Loggar från Docker:
# Realtidslogg
docker-compose logs -f app
# Senaste 100 rader
docker-compose logs --tail=100 app
# Exportera loggar
docker-compose logs app > logs_$(date +%Y%m%d).txt
Centraliserad loggning med Loki (valfritt):
docker run -d --name=loki -p 3100:3100 grafana/loki
Metriker med Prometheus (Avancerat)
docker-compose.monitoring.yml:
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
Skalning
Vertikal skalning (Enklast)
Öka resurser för Docker-containrar:
# docker-compose.yml
services:
app:
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '1.0'
memory: 2G
Horisontell skalning (Avancerat)
Kräver:
- Load balancer (Nginx/HAProxy)
- Delad fillagring (S3/NFS)
- Redis för sessions
Exempel med Docker Compose:
services:
app:
image: kunskapsportal:latest
deploy:
replicas: 3
environment:
- REDIS_URL=redis://redis:6379
redis:
image: redis:alpine
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
Database scaling
PostgreSQL replication:
- Primary/Replica setup
- Read replicas för frågor
- Automatisk failover
Managed databases:
- AWS RDS
- DigitalOcean Managed PostgreSQL
- Supabase
Uppdateringar
Manuell uppdatering
# Hämta senaste kod
git pull origin main
# Bygg om Docker-imagen
docker-compose build
# Stoppa gamla containrar
docker-compose down
# Starta nya containrar
docker-compose up -d
# Verifiera
docker-compose logs -f app
Automatiska uppdateringar (Watchtower)
Lägg till i docker-compose.yml:
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_POLL_INTERVAL=86400 # 24 timmar
Checklista för produktion
- Starka lösenord och API-nycklar
- SSL/TLS-certifikat installerat
- Brandvägg konfigurerad
- Fail2ban aktiverat
- Automatiska backups schemalagda
- Uptime monitoring aktiverat
- Loggning konfigurerad
- Error tracking (Sentry/etc.)
- DNS konfigurerat korrekt
- Email för notifikationer
- Dokumentation för driftsättning
- Runbook för incidenter
Support
Produktionsproblem?
- Öppna issue: https://github.com/Falkenbergs-kommun/kunskapsportal/issues
- Kritiska säkerhetsproblem: utvecklingsavdelningen@falkenberg.se
Professionell support: Kontakta Falkenbergs kommun Utvecklingsavdelningen för support.