Umzug von Vercel zu einem EU-Hoster
Eine Next.js-App von Vercel zu blitz.cloud in Deutschland umziehen: Standalone-Docker-Build, Umgebungsvariablen, Datenbank und was noch nicht mitkommt.
Für den Umzug einer Next.js-App von Vercel zu blitz.cloud baust du sie mit output: "standalone" zu einem Docker-Image, das auf Port 8080 ohne Root läuft, lädst das Image auf Docker Hub hoch, rollst es auf blitz.cloud aus und überträgst deine Umgebungsvariablen. Die App läuft dann auf Servern in Falkenstein, betrieben von einem deutschen Unternehmen. Ob sich der Tausch lohnt, hängt davon ab, wie viel deine App von Vercels Plattform nutzt. Lies deshalb den nächsten Abschnitt, bevor du anfängst.
Was sauber umzieht und was noch nicht
Was heute mitkommt:
- Next.js-Apps, die als Node-Server laufen: Server Components, Server Actions, Route Handler in
app/apiund Middleware (seit Next.js 16 "proxy" genannt), die im Node-Server läuft. - Komplett statische Seiten, auch Builds mit
output: "export". - Umgebungsvariablen und Secrets.
- Eine PostgreSQL-, MySQL/MariaDB- oder Redis-Datenbank, solange die App selbst deine Daten einspielen kann (siehe unten).
Worauf du verzichtest, zumindest vorerst:
- Deploys bei
git pushund Preview Deployments pro Pull Request. blitz.cloud kann noch nicht aus GitHub bauen; du lädst ein Image hoch. - Deine eigene Domain. Apps liegen unter
yourname.blitz.cloud, bis eigene Domains kommen. - Ein globales Edge-Netz. blitz.cloud liefert aus einem Rechenzentrum in Deutschland aus. Besucher in Europa merken nichts, Besucher in Australien schon.
- Edge Functions und Vercel-spezifische Dienste wie Vercel Blob, Edge Config, KV, Cron Jobs und Web Analytics.
- Automatische Bildoptimierung als verwalteter Dienst.
next/imagefunktioniert im Standalone-Modus weiter, aber das Skalieren läuft in deiner App und braucht ihren Arbeitsspeicher. - Skalieren auf null und Abrechnung pro Anfrage. Eine App auf blitz.cloud ist ein dauerhaft laufender Prozess mit 128 MB reserviertem Arbeitsspeicher und einer Obergrenze von 512 MB.
Wenn deine App vor allem von der zweiten Liste lebt, bleib bei Vercel oder schau dir einen Hoster an, der diese Funktionen heute schon hat. Ist deine App ein normaler Next.js-Server mit Datenbank und der Grund für den Umzug ist Hosting in der EU, mach weiter.
Schritt 1: ein Standalone-Image bauen
Schalte die Standalone-Ausgabe in next.config.mjs ein:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
};
export default nextConfig;
Lege ein Dockerfile neben package.json an:
FROM node:22.23.2-alpine3.24 AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:22.23.2-alpine3.24 AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
FROM node:22.23.2-alpine3.24
WORKDIR /app
ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 PORT=8080 HOSTNAME=0.0.0.0
# standalone/ holds server.js and the node_modules it needs; static files and
# public/ are not copied into it by Next, so they are added here.
COPY --from=build --chown=1000:1000 /app/.next/standalone ./
COPY --from=build --chown=1000:1000 /app/.next/static ./.next/static
COPY --from=build --chown=1000:1000 /app/public ./public
USER 1000
EXPOSE 8080
CMD ["node", "server.js"]
Und eine .dockerignore, damit deine lokale Build-Ausgabe nicht im Image landet:
node_modules
.next
.git
HOSTNAME=0.0.0.0 sorgt dafür, dass der Server auf allen Schnittstellen lauscht statt nur innerhalb des Containers. Mit den --chown-Flags darf die App ihren Cache als Benutzer 1000 schreiben.
Wir haben dieses Dockerfile mit Next.js 16.3.5 getestet: Eine Seite, ein Route Handler und eine Datei aus public/ antworteten alle mit 200, als Benutzer 1000 und ohne Capabilities. Wenn du pnpm oder yarn nutzt, tausch den Installationsbefehl und den Namen der Lockdatei aus.
Schritt 2: lokal so testen, wie blitz.cloud es startet
docker build --platform linux/amd64 -t yourname/my-next-app:1.0.0 -t yourname/my-next-app:latest .
docker run --rm --user 1000:1000 --cap-drop ALL --security-opt no-new-privileges \
-p 8080:8080 --env-file .env.production.local yourname/my-next-app:1.0.0
Klick dich unter http://localhost:8080 durch die App, bevor du weitermachst. Alles, was hier kaputtgeht, etwa eine fehlende Variable oder ein Schreibzugriff auf einen Ordner, den Benutzer 1000 nicht anfassen darf, geht auf blitz.cloud genauso kaputt.
Schritt 3: die Umgebungsvariablen übertragen
Hol die Produktionsvariablen mit der CLI von Vercel:
vercel env pull .env.production.local --environment=production
Es gibt eine Falle. Variablen, die mit NEXT_PUBLIC_ beginnen, werden beim Build fest ins JavaScript geschrieben. Sie danach auf blitz.cloud zu setzen, ändert im Browser nichts. Gib sie stattdessen an docker build weiter, zum Beispiel mit ARG NEXT_PUBLIC_API_URL und ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL in der Build-Stufe. Reine Server-Variablen werden beim Start der App gelesen, die kommen in den Tab "Environment" auf blitz.cloud.
Entferne alles Vercel-Spezifische wie VERCEL_URL und ersetze Code, der sich darauf verlässt, durch deine neue Adresse.
Schritt 4: hochladen und ausrollen
docker push --all-tags yourname/my-next-app
Das Docker-Hub-Repository muss öffentlich sein. Dein Image enthält deine gebaute App, jeder kann es also herunterladen; halte Secrets aus dem Image heraus und in Umgebungsvariablen.
- Klicke im blitz.cloud-Dashboard auf "Host something new" (Neues hosten) und wähle "An app that is already packaged up" (eine fertig verpackte App).
- Suche nach
yourname/my-next-appund wähle1.0.0. - Prüfe, dass unter "What this app needs" Port 8080 steht. Nutzt die App eine Datenbank, öffne "Advanced settings" und schalte "Needs a database" ein.
- Wähle eine Adresse und klicke auf "Put it online".
- Öffne den Tab "Environment" der App, trag deine serverseitigen Variablen ein und starte die App neu.
Schritt 5: die Datenbank
Liegt deine Datenbank bei Vercel Postgres, Neon oder Supabase, ist die schonendste Lösung, sie vorerst dort zu lassen und DATABASE_URL darauf zeigen zu lassen. Apps auf blitz.cloud dürfen ausgehende Verbindungen aufbauen. Beachte: Damit bleiben deine Daten außerhalb der EU, wenn diese Datenbank außerhalb der EU liegt, und genau das willst du vielleicht beheben.
Um die Daten zu blitz.cloud zu holen, legst du die App mit einer PostgreSQL-Datenbank an und spielst einen Dump aus der App heraus ein. Von deinem Laptop aus kannst du psql heute nicht mit einer blitz.cloud-Datenbank verbinden. Ein einmaliger Import muss also Teil der App sein, etwa ein Migrationsskript, das beim ersten Start Daten wiederherstellt. Bei einer großen Datenbank ist das umständlich, und es lohnt sich, auf den Zugriff von außen zu warten.
Checkliste
-
output: "standalone"gesetzt und das Image baut fürlinux/amd64 - Image läuft lokal mit
--user 1000:1000 --cap-drop ALL -
NEXT_PUBLIC_-Variablen beim Build übergeben - Serverseitige Variablen im Tab "Environment" eingetragen, App neu gestartet
- Keine reinen Vercel-APIs mehr (Blob, KV, Edge Config,
VERCEL_URL) - Datenbank von blitz.cloud aus erreichbar oder umgezogen
- Links und OAuth-Callback-URLs auf die neue
*.blitz.cloud-Adresse umgestellt - Altes Vercel-Projekt läuft weiter, bis das neue echten Traffic ausgeliefert hat
Warum überhaupt umziehen?
Vercel ist ein US-Unternehmen. Daten, die es verarbeitet, fallen deshalb unter US-Recht, einschließlich des CLOUD Act, egal in welcher Region sie liegen. blitz.cloud wird von der BlitzWorks UG (haftungsbeschränkt) in Regensburg auf Hetzner-Servern in Deutschland betrieben, und keines der beiden Unternehmen hat einen US-Mutterkonzern. Für manche Teams klärt das eine DSGVO-Frage. Mehr dazu unter Datenstandort und DSGVO. Einen Funktionsvergleich Seite an Seite gibt es unter blitz.cloud vs. Vercel.
Stell heute deine erste App online.
Kostenloser Tarif, keine Kreditkarte, keine Warteliste.
Kostenloses Konto anlegen