Deployment og rollback¶
Produktionsmiljøet app.groopswork.dk drives lokalt på build-serveren og er
autoritativt. Stakken køres manuelt:
docker compose -f docker-compose.yml -f compose.ci.yml -p groopworks-main up -d
Secrets (DB_PASSWORD, BETTER_AUTH_SECRET, ONLYOFFICE_JWT_SECRET m.fl.) læses fra
en root-only env-fil i /srv/groopworks (ikke fra repoet). Et git push til
main deployer derfor ikke automatisk til produktion — pipelinen er kun en
test+build-gate, og deploy_production er manuelt (se nedenfor). Dermed kan
en push aldrig overskrive den lokale stak.
Hver review-branch får sin egen isolerede stak på app-<branch-slug>.groopswork.dk.
Coolify-opsætningen for eksterne miljøer er beskrevet i DEPLOY.md i repo-roden.
Advarsel — projektnavn ved lokal dev:
stop_reviewkørerdocker compose -p groopworks-<branch-slug> down -v(fjerner volumener). En lokal dev-stak må derfor aldrig heddegroopworks-<branch-slug>— brug projektnavnetgwdev(docker compose -p gwdev ...), så en pipeline aldrig river dine data ned. Produktionen bruger navnetgroopworks-main.
Pipeline¶
test → build → docs → deploy — ingen test-jobs har allow_failure.
Pipelinen er en gate: den tester og bygger images, men rører ikke den lokale
produktions-stak. deploy_production er manuelt (when: manual,
allow_failure: true) og bevaret til lejlighedsvis manuel brug via GitLab-UI'et
— det kører aldrig automatisk på main.
| Stage | Job | Indhold |
|---|---|---|
| test | backend-test |
tsc --noEmit (blokerende), prisma migrate deploy, 124 tests (E2E-flows, edge cases, sikkerhed/multi-tenancy/DS-JWT) |
| test | frontend-build |
ADR-002 token-gate, build-markør-assertion, lint, vite-build |
| build | build-images |
Bygger og pusher images tagget <branch-slug>/{frontend,backend}:<commit-sha> |
| docs | docs-build |
mkdocs build --strict — fejler på døde links og sider uden nav |
| deploy | deploy_review (kun review-branches) |
docker compose pull && up -d med commit-taggede images |
| deploy | deploy_production (kun main, manuel) |
Rører ikke den lokale stak automatisk; kun til manuel brug |
| deploy | deploy_docs (kun main) |
Publicerer MkDocs-sitet på https://docs.groopswork.dk |
Migrationer¶
- Migrationer kører automatisk ved boot (
entrypoint.sh→prisma migrate deploy) og er forward-only. Skriv aldrig destruktive migrationer uden en ledsagende plan i MR'en. - Verificeret mod produktionskopi 2026-06-12: dump af
groopworks-main-db-1gendannet i isoleret postgres,prisma migrate deploy→ 8 migrationer fundet, "No pending migrations",migrate status→ "Database schema is up to date". - Test selv en migration mod prod-kopi:
sudo docker exec groopworks-main-db-1 pg_dump -U postgres -d groopswork_db -F c -f /tmp/prodcopy.dump
sudo docker cp groopworks-main-db-1:/tmp/prodcopy.dump /tmp/
sudo docker run -d --rm --name prodcopy --network gw-test \
-e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=test -e POSTGRES_DB=groopswork_db postgres:14-alpine
sudo docker cp /tmp/prodcopy.dump prodcopy:/tmp/ \
&& sudo docker exec prodcopy pg_restore -U postgres -d groopswork_db --no-owner /tmp/prodcopy.dump
sudo docker run --rm --network gw-test -v "$PWD/backend":/app -w /app \
-e DATABASE_URL=postgresql://postgres:test@prodcopy:5432/groopswork_db \
oven/bun:1 sh -c "bunx prisma migrate deploy && bunx prisma migrate status"
Rollback-plan¶
Images er taggede med commit-SHA (<registry>/<branch-slug>/backend:<sha>),
så en rollback er en redeploy af den forrige SHA — ingen rebuild.
1. Rollback af applikationen (minutter)¶
# Find den forrige grønne main-pipeline og dens SHA i GitLab (Pipelines → main)
PREV_SHA=<kort-sha>
cd /sti/til/groopworks-new # repo-checkout på build-serveren
export CI_REGISTRY_IMAGE=<registry>/root/groopworks-new \
CI_COMMIT_REF_SLUG=main CI_COMMIT_SHORT_SHA=$PREV_SHA \
DEPLOY_HOST=app.groopswork.dk ROUTER_NAME=groopworks-prod \
ROUTER_RULE='Host(`app.groopswork.dk`)'
docker compose -f docker-compose.yml -f compose.ci.yml -p groopworks-main \
pull frontend backend
docker compose -f docker-compose.yml -f compose.ci.yml -p groopworks-main \
up -d frontend backend
Alternativt via GitLab-UI'et: åbn den forrige grønne pipeline på main og
kør deploy_production-jobbet igen (Retry) — det puller dén pipelines
SHA-taggede images.
2. Rollback af databasen (kun hvis en migration skal fortrydes)¶
Migrationer er forward-only, så database-rollback = gendan fra dump:
# FØR enhver risikabel deploy: tag en dump
sudo docker exec groopworks-main-db-1 pg_dump -U postgres -d groopswork_db -F c -f /tmp/pre-deploy.dump
# Rollback: stop backend, gendan, start forrige image
docker compose -p groopworks-main stop backend
sudo docker exec groopworks-main-db-1 dropdb -U postgres groopswork_db
sudo docker exec groopworks-main-db-1 createdb -U postgres groopswork_db
sudo docker exec groopworks-main-db-1 pg_restore -U postgres -d groopswork_db --no-owner /tmp/pre-deploy.dump
# ...og deploy derefter den forrige app-SHA (trin 1)
Vigtigt: data oprettet mellem dump og rollback går tabt — tag altid en frisk dump umiddelbart før deploys med migrationer, og hold vinduet kort.
3. Dokumentfiler (uploads-volumen)¶
backend-servicens uploads ligger i en navngiven docker-volume og rulles
IKKE tilbage sammen med databasen. Dokument-records uden fil falder tilbage
til en blank skabelon (serveDocument), så uoverensstemmelser er ufarlige,
men tag en kopi af volumen sammen med dumpen ved større operationer:
sudo docker run --rm --volumes-from groopworks-main-backend-1 -v /tmp:/backup \
alpine tar czf /backup/uploads-$(date +%F).tgz /app/src/uploads
Alle upload-kategorier (afleveringer, opgave-vedhæftninger, gruppe-vedhæftninger
og arbejdsrums-dokumenter) ligger under den samme volume-monterede rod
/app/src/uploads, defineret ét sted i backend/src/config/uploads.ts. Skriv
aldrig uploads til <cwd>/uploads (uden src/) — den sti ligger uden for
volumen og tabes ved redeploy.
Sundhedstjek efter deploy/rollback¶
curl -s https://app.groopswork.dk/api/health # {"status":"ok",...}
curl -s https://app.groopswork.dk/api/health/deep # db + onlyoffice