Hopp til hovedinnhold

Fra Vercel til Hetzner og Coolify: hvorfor jeg flyttet hjem

Vercel er nydelig å komme i gang med, men på et tidspunkt begynte jeg å betale for magi jeg ikke trengte, og data jeg ville holde i EU. Her er historien om hvordan jeg flyttet Søknadsbasen til en enkelt Hetzner-server med Coolify, hva som faktisk var vanskelig, og hva jeg sitter igjen med.

Hvorfor i det hele tatt flytte?

Jeg har ingenting vondt å si om Vercel. Det er kanskje den beste utvikleropplevelsen som finnes, og for et sideprosjekt er gratis-nivået mer enn nok. Men Søknadsbasen vokste fra "sideprosjekt" til "noe folk faktisk betaler for", og da begynte tre ting å skurre:

  1. Kostnad og forutsigbarhet. Serverless er billig helt til det ikke er det. PDF-generering med headless Chrome spiser minne, og hver funksjon med høyt minnetak koster.
  2. Datalagring i EU. Jeg vil kunne si ærlig på personvernsiden at data ligger i Europa. Med en egen server i Tyskland vet jeg nøyaktig hvor ting er.
  3. Ting serverless ikke liker. Jeg har en WebSocket-tjeneste for sanntids-samskriving av CV. Langtlevende tilkoblinger og serverless er ikke bestevenner.

Løsningen ble en enkelt Hetzner-server i Falkenstein, med Coolify som "min egen lille Vercel" oppå.

Hva er Coolify?

Coolify er en åpen kildekode-PaaS du kjører selv. Du peker den på et Git-repo, den bygger med Nixpacks eller en Dockerfile, og håndterer domener, Let's Encrypt-sertifikater, miljøvariabler og deploys. Du får mye av Vercel-følelsen, men på maskinvare du eier, til en fast månedspris.

For min del: én CPX41 hos Hetzner kjører Coolify, selve Next.js-appen, og collab-serveren, alt på samme boks.

Det som var enkelt

Selve serveren tar ti minutter. Bestill, SSH inn, og kjør:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

Coolify-dashbordet kommer opp, du kobler GitHub, velger repo, og er nesten i gang. Sertifikater og domener er et par klikk.

Det som faktisk krevde tankearbeid

Her er de tingene jeg skulle ønske noen hadde sagt på forhånd.

1. Next.js må bygge "standalone"

For et slankt Docker-image satte jeg:

// next.config.ts
const nextConfig = {
  output: "standalone",
  // ...
};

Da legger Next alt serveren trenger i .next/standalone, og imaget blir lite.

2. NEXT_PUBLIC-variabler bakes inn ved BUILD

Dette er den klassiske fellen. Alt som starter med NEXT_PUBLIC_ inlines i klient-bundelen når du bygger, ikke ved oppstart. På Vercel skjer det automatisk. I en Dockerfile må du sende dem inn som build-args:

ARG NEXT_PUBLIC_SUPABASE_URL
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL
RUN npx next build

Glemmer du dette, bygger appen fint, men nettleseren får tomme verdier og alt knekker i produksjon.

3. Headless Chrome i en container

PDF-ene mine lages med Puppeteer. På Vercel brukte jeg en serverless-spesifikk Chromium-pakke. I en vanlig container vil du heller installere system-Chromium og peke Puppeteer dit:

RUN apt-get update && apt-get install -y --no-install-recommends \
      chromium fonts-liberation libnss3 libgbm1 libasound2 # ...
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium

Og koden velger riktig nettleser basert på miljø, så lokal utvikling og produksjon oppfører seg likt.

4. Migrasjoner ved oppstart, ikke ved build

Build-steget har ikke tilgang til databasen, og det bør det heller ikke ha. Jeg kjører derfor migrasjonene i en entrypoint når containeren starter:

#!/bin/sh
set -e
prisma migrate deploy
exec node server.js

5. Cron finnes ikke gratis lenger

Vercel Cron var bare noen linjer i vercel.json. Med Coolify ble det "Scheduled Tasks" som kaller mine egne endepunkter med en hemmelig nøkkel:

curl -fsS -H "Authorization: Bearer $CRON_SECRET" \
  https://soknadsbasen.no/api/cron/jobs-sync

Mens jeg var der gjorde jeg cron-rutene "fail-closed": uten riktig hemmelighet svarer de 401. Bedre at en glemt variabel stopper jobben enn at hvem som helst kan trigge den.

6. Den langtlevende WebSocket-tjenesten

Sanntids-samskrivingen (Yjs over Hocuspocus) er en egen prosess som bare skal stå og kjøre. Det er nettopp denne typen ting serverless gjør vanskelig, og som en alltid-på container gjør trivielt. På Coolify ble det bare en ekstra "resource" med eget subdomene og egen Dockerfile.

Ikke glem flyttelasset

Selve appen er halve jobben. Resten er det rundt:

  • Hent miljøvariablene ut av Vercel: vercel env pull.
  • Pek Stripe- og e-post-webhooks mot det nye domenet, og oppdater signeringshemmelighetene.
  • Legg det nye domenet i Supabase sin auth-konfigurasjon.
  • Senk DNS-TTL et døgn før du flytter, så cutover går raskt.

Og det aller viktigste: behold Vercel kjørende til alt er verifisert. Jeg testet innlogging, AI, PDF og en ekte testbetaling på det nye oppsettet før jeg rørte DNS. Da er rollback bare å peke domenet tilbake.

Hva sitter jeg igjen med?

Jeg eier operasjonene nå. Det er både poenget og prisen. Jeg må selv tenke på oppdateringer, backup og overvåking, ting Vercel skjulte for meg. Til gjengjeld har jeg forutsigbar kostnad, data i EU, full kontroll på langtlevende prosesser, og en setup jeg forstår hele veien ned.

For et lite, betalende produkt var det verdt det. For en helg-prototype hadde jeg blitt på Vercel.

Anbefaling

Hvis du vurderer det samme: start med å gjøre appen Docker-klar mens du fortsatt er på Vercel. Få docker build til å funke lokalt først. Når imaget bygger og kjører på maskinen din, er resten bare en server, et domene og litt tålmodighet.

Hvis du likte dette

Abonnér via RSS eller JSON Feed, eller ta kontakt.

Del på: LinkedIn · Redi AS · Spotify · Instagram · Wikidata