Design wechseln

Weg von Vercel: Der vollständige Leitfaden zur Next.js Docker-Self-Hosting

Easton editorial illustration: performance inspection lens

Ende letzten Monats, wie üblich die Vercel-Abrechnung geöffnet. 47,32 $.

Kurz im Kopf gerechnet: Der Blog-Traffic stieg diesen Monat vielleicht um 20 % – warum verdoppelt sich die Rechnung? In den Details: Serverless-Function-Aufrufe – eine API-Route ohne sauberes Caching lief bei jedem Seiten-Reload dreimal.

Vercels Developer Experience ist wirklich angenehm: git push, automatisches Deployment, globales Edge-Netzwerk, viele Features out of the box. Sobald etwas Traffic kommt, steigen die Kosten sprunghaft. Der Pro-Plan für 20 $ ist nur der Einstieg – teuer werden die nutzungsbasierten Posten.

In dem Moment war klar: Zeit, das Projekt woanders hin zu verlagern.

Dieser Artikel dokumentiert die vollständige Migration eines Next.js-Projekts von Vercel zu Docker-Self-Hosting. Fallstricke, gelesene Docs und getestete Konfigurationen – alles hier zusammen. Wer Self-Hosting erwägt oder schon probiert hat und seltsame Probleme sieht (404 bei statischen Assets, kaputtes Streaming), findet hoffentlich Antworten.

$35-50
Vercel Monatskosten
Trafficabhängig
$12
Self-Hosting Monatskosten
Fixkosten
$300-500
Jährliche Ersparnis
Mehrere Projekte möglich
200MB
Docker-Image-Größe
Nach dreistufigem Build
Source: Praxisdaten

Warum weg von Vercel?

Klarstellung vorweg: Vercel soll hier nicht schlechtgeredet werden. Für viele Szenarien bleibt es die beste Wahl – Enterprise-Projekte, globales Edge-Netzwerk oder Teams ohne Ops-Kapazität. Für persönliche Projekte und kleine Teams sind die Kosten aber spürbar.

Vercels Preismodell

Der Free-Tier wirkt großzügig: 100 GB Bandbreite, 1 Mio. Edge Requests. Für ein Projekt mit etwas Traffic reicht das schnell nicht. Mit Pro (20 $/Monat) beginnt erst der Einstieg:

  • Serverless Function-Aufrufe: nach 1 Mio. nutzungsbasiert
  • Edge-Function-Laufzeit: nach 1 Mio. GB-s zusätzliche Kosten
  • Bildoptimierung: nach 5.000 Aufrufen pro Stück
  • Bandbreite: nach 1 TB pro GB

Schwierig ist die Vorhersage. Eine schlecht gecachte API-Route oder aggressive Crawler lassen die Rechnung explodieren.

Wie viel spart Self-Hosting?

Rechenbeispiel: Das Projekt kostete auf Vercel etwa 35–50 $/Monat, trafficabhängig. Nach dem Umzug auf einen DigitalOcean-Server für 12 $/Monat:

  • Server: 12 $/Monat (2 vCPU, 4 GB RAM – reicht für zwei bis drei Next.js-Apps)
  • Cloudflare CDN: kostenlos (wird ohnehin genutzt)
  • Zusatzspeicher: 0 $ (lokale Platte reicht)

Monatlich 25–40 $ Ersparnis, im Jahr 300–500 $. Entscheidend: fixe Kosten – keine Explosion bei Traffic-Spitzen.

Wann passt Self-Hosting?

Nicht für alle. Sinnvoll, wenn:

  • ✅ Linux- und Docker-Grundlagen vorhanden
  • ✅ Traffic relativ stabil, kein globales Edge-Netzwerk nötig
  • ✅ 5–10 Minuten manuelles Deployment akzeptabel
  • ✅ Budget knapp (Side Projects, frühe Startup-Phase)

Besser bei Vercel bleiben, wenn:

  • ❌ Kein Ops-Know-how und keine Lust darauf
  • ❌ Starke Traffic-Schwankungen, Auto-Scaling nötig
  • ❌ Vercel Analytics, Edge Config usw. gebraucht werden
  • ❌ Budget da, Entwicklungseffizienz wichtiger

Erst abwägen, dann handeln – nicht zum Sparen unnötig leiden.

Next.js Docker – Kernkonfiguration

Drei zentrale Punkte für Next.js mit Docker. Wenn diese sitzen, laufen die meisten Deployments stabil.

1. Standalone-Ausgabemodus

Der wichtigste Schritt. Standardmäßig erzeugt next build viele Dateien inklusive vollem node_modules – im Container riesig und langsam.

In next.config.js eine Zeile ergänzen:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
}

module.exports = nextConfig

Nach npm run build liegt .next/standalone bereit:

  • server.js: Startskript
  • Reduziertes node_modules: nur Laufzeit-Pakete
  • Anwendungscode

Wichtig: Standalone kopiert public und .next/static nicht automatisch. Beides muss manuell ins Standalone-Verzeichnis – sonst 404 für alle statischen Assets. Zwei Tage Debugging, bis das klar war.

2. Mehrstufiges Dockerfile

Das produktive Dockerfile mit Kommentaren:

# ============ Phase 1: Abhängigkeiten ============
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Nur Manifeste kopieren – Docker-Cache nutzen
COPY package.json package-lock.json ./
RUN npm ci

# ============ Phase 2: App bauen ============
FROM node:20-alpine AS builder
WORKDIR /app

# Abhängigkeiten und Quellcode
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Build-Umgebungsvariablen (falls nötig)
ENV NEXT_TELEMETRY_DISABLED=1

# Build
RUN npm run build

# ============ Phase 3: Produktion ============
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# Nicht-Root-Benutzer (Sicherheit)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# public (statische Assets)
COPY --from=builder /app/public ./public

# Standalone-Ausgabe
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# static (CSS/JS-Build-Artefakte)
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

# Start
CMD ["node", "server.js"]

Kurz erklärt:

  1. Drei Phasen: Abhängigkeiten, Build, Laufzeit getrennt – finales Image nur mit Laufzeit-Dateien, von 1,5 GB auf 200 MB
  2. COPY --from=builder /app/public: sonst kein favicon, robots.txt
  3. COPY ./.next/static: ohne diese Zeile 404 für JS/CSS
  4. Nicht-Root: Sicherheitsbest Practice – in Produktion nicht als root laufen

3. Fallstricke bei Umgebungsvariablen

Next.js unterscheidet zwei Arten:

  • Build-Zeit: Präfix NEXT_PUBLIC_, wird in den Code eingebaut
  • Laufzeit: serverseitig, z. B. Datenbank-URL

Im Standalone-Modus funktioniert runtimeConfig nicht. Offiziell empfohlen: App Router:

// app/api/example/route.ts
export async function GET() {
  // Direkt aus process.env lesen
  const dbUrl = process.env.DATABASE_URL
  // ...
}

Umgebungsvariablen beim Docker-Start:

docker run -p 3000:3000 \
  -e DATABASE_URL="postgres://..." \
  -e API_KEY="xxx" \
  your-image-name

Oder mit docker-compose.yml:

version: '3.8'
services:
  nextjs:
    image: your-image-name
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: "postgres://..."
      API_KEY: "xxx"
    restart: unless-stopped

Hinweis: NEXT_PUBLIC_* muss beim Build gesetzt sein – zur Laufzeit nicht änderbar. Dynamische Konfiguration nur über serverseitige Variablen.

Reverse Proxy – das Wichtigste

Den Next.js-Container direkt ins Internet stellen – besser nicht. Ungeschützte Node.js-Apps halten bei Angriffen und Slowloris nicht lange durch. Reverse Proxy ist Pflicht, kein Nice-to-have.

Warum ein Reverse Proxy?

  1. Sicherheit: bösartige Requests filtern, Rate Limiting, DDoS-Schutz
  2. HTTPS: zentral SSL-Zertifikate verwalten
  3. Mehrere Apps: eine Maschine, mehrere Projekte per Domain/Pfad
  4. Static-Cache: Entlastung des App-Servers

Hier: Nginx – stabil. Für einfachere Config: Caddy (automatisches HTTPS, weniger Boilerplate).

Nginx-Beispielkonfiguration

server {
    listen 80;
    server_name yourdomain.com;
    
    # HTTPS erzwingen (wenn SSL konfiguriert)
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;
    
    # SSL (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
    # Reverse Proxy zum Next.js-Container
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        
        # Erforderliche Header
        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;
        
        # Wichtig: Buffering aus – Streaming
        proxy_buffering off;
        proxy_cache off;
        proxy_set_header X-Accel-Buffering no;
        
        # WebSocket (falls nötig)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    
    # Static-Cache (optional, empfohlen)
    location /_next/static/ {
        proxy_pass http://localhost:3000;
        proxy_cache_valid 200 60m;
        add_header Cache-Control "public, max-age=3600, immutable";
    }
}

Drei Pflicht-Einstellungen:

  1. proxy_buffering off: sonst hängt Streaming
  2. X-Accel-Buffering: no: Nginx soll den Body nicht puffern
  3. WebSocket: bei Socket.io oder Echtzeit – Upgrade-Header setzen

Caddy – vereinfacht

Wenn Nginx zu viel Config ist:

yourdomain.com {
    reverse_proxy localhost:3000 {
        # Caddy puffert standardmäßig nicht
    }
}

Fertig. Caddy holt und erneuert Let’s-Encrypt-Zertifikate automatisch.

Docker Compose mit Nginx

Nginx containerisieren erleichtert das Management:

version: '3.8'
services:
  nextjs:
    build: .
    restart: unless-stopped
    environment:
      DATABASE_URL: "postgres://..."
    # Nicht auf Host exposen – nur nginx
    expose:
      - "3000"
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./certs:/etc/letsencrypt
    depends_on:
      - nextjs
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

Bei nextjs: expose statt ports – nur Container im gleichen Netz erreichen den Dienst, sicherer.

Streaming-Rendering reparieren

Ein ganzer Tag Debugging: Lokal lief der KI-Chat mit Streaming, in Docker kam alles auf einmal oder hing.

Symptome

Typisch:

  • OpenAI-/Anthropic-Streaming antwortet nicht
  • Server-Sent Events (SSE) ohne Echtzeit-Push
  • Seite aktualisiert plötzlich, kein Wort-für-Wort-Output

Lokal mit npm run dev normal – in Produktion kaputt.

Ursachen

Zwei häufige Stellen:

  1. Reverse-Proxy-Buffering: Nginx puffert standardmäßig, bis der Body vollständig ist
  2. Next.js Runtime: API-Routen ohne Edge Runtime unterstützen Streaming manchmal nicht

Lösung 1: Nginx

Die drei Zeilen aus dem Reverse-Proxy-Kapitel noch einmal:

proxy_buffering off;
proxy_cache off;
proxy_set_header X-Accel-Buffering no;

In den location /-Block. Danach Nginx neu laden:

nginx -t  # Syntax prüfen
nginx -s reload  # neu laden

Lösung 2: Edge Runtime

Für Streaming-APIs (z. B. KI-Chat) oben in der Datei:

// app/api/chat/route.ts
export const runtime = 'edge'

export async function POST(req: Request) {
  const stream = new ReadableStream({
    async start(controller) {
      // Streaming-Logik
      const response = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [...],
        stream: true,
      })

      for await (const chunk of response) {
        controller.enqueue(chunk.choices[0]?.delta?.content || '')
      }
      
      controller.close()
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  })
}

Edge Runtime ist leichtgewichtig und für Streaming optimiert – in Docker oft stabiler.

Verifikation

Mit curl testen – zeilenweise Ausgabe bedeutet Erfolg:

curl -N http://yourdomain.com/api/chat \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello"}'

-N deaktiviert Buffering – Inhalt sollte schrittweise kommen, nicht plötzlich alles.

Immer noch kaputt?

  1. Cloudflare-Proxy: orange Wolke puffert – deaktivieren (grau) oder Pro (Streaming)
  2. Docker-Healthcheck: kann Streaming stören – healthcheck in docker-compose.yml prüfen
  3. Load Balancer: davor ebenfalls Buffering konfigurieren

Häufige Probleme – Diagnose und Fix

Typische Fallstricke und häufige Fragen – decken etwa 80 % der Deployment-Fehler ab.

Problem 1: Statische Assets 404

Symptom: Seite lädt, Styles fehlen, Konsole voller 404 unter /_next/static/...

Ursache: .next/static im Dockerfile nicht korrekt kopiert.

Fix: Diese Zeilen im Dockerfile:

COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

Bei weiterhin 404 – Rechte prüfen:

COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

Problem 2: Docker-Build schlägt fehl

Symptom: docker build meldet „Could not find a production build in the ‘.next’ directory“

Ursache: .dockerignore falsch oder Build-Reihenfolge.

Fix: .dockerignore anlegen:

.next
node_modules
.git
.env*.local
out
.DS_Store
*.log

.next ausschließen – Build passiert im Container.

Problem 3: Umgebungsvariablen greifen nicht

Symptom: process.env.DATABASE_URL ist undefined

Ursache: falsche Übergabe oder Build vs. Laufzeit verwechselt.

Fix:

  1. Laufzeit (DB, API-Keys): docker run -e oder docker-compose.yml:

    docker run -e DATABASE_URL="..." your-image
  2. Build-Zeit (NEXT_PUBLIC_*): beim docker build:

    docker build --build-arg NEXT_PUBLIC_API_URL="https://api.example.com" .

    Im Dockerfile:

    ARG NEXT_PUBLIC_API_URL
    ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

Problem 4: Speicher beim Build

Symptom: Build hängt oder „JavaScript heap out of memory“

Ursache: Node.js-Standardlimit, große Next.js-Projekte brauchen mehr RAM.

Fix: In der builder-Phase:

# builder-Phase
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN npm run build

Oder BuildKit-Ressourcenlimit:

docker build --memory=8g --memory-swap=8g -t your-image .

Problem 5: Container läuft, Port nicht erreichbar

Symptom: Container healthy, http://localhost:3000 verweigert Verbindung.

Ursache: Next.js lauscht standardmäßig auf 127.0.0.1 – im Container von außen nicht erreichbar.

Fix: Im Dockerfile:

ENV HOSTNAME="0.0.0.0"
ENV PORT=3000

Oder beim Start:

docker run -p 3000:3000 -e HOSTNAME="0.0.0.0" your-image

Schnelle Diagnose

Bei Problemen zuerst:

# 1. Container läuft?
docker ps

# 2. Logs
docker logs <container-id>

# 3. Dateistruktur im Container
docker exec -it <container-id> sh
ls -la .next/
ls -la public/

# 4. Dienst im Container
docker exec -it <container-id> wget -O- http://localhost:3000

# 5. Port-Mapping
docker port <container-id>

Fazit

Migration von Vercel zu Docker-Self-Hosting war weniger schlimm als befürchtet. Anfangs etwas Setup – danach geringer Wartungsaufwand. Aktuell: 12 $/Monat fix, drei Next.js-Projekte, keine Rechnungsüberraschungen.

Die drei Kernpunkte noch einmal:

  1. Standalone – eine Zeile in next.config.js, public und .next/static manuell kopieren
  2. Mehrstufiges Dockerfile – drei Phasen, finales Image ~200 MB, schneller Start
  3. Reverse Proxy – Nginx ohne Buffering (proxy_buffering off), sonst kein Streaming

Bei Streaming-Problemen: in 99 % der Fälle Reverse-Proxy-Buffering – plus export const runtime = 'edge' löst es meist.

Vercel vs. Self-Hosting

DimensionVercelDocker Self-Hosting
Deployment⚡️ git push🐢 5–10 Min. manuell
Developer Experience🌟 Preview, Logs, Analytics🔧 Monitoring selbst einrichten
Kosten💸 20 $+/Monat, steigt mit Traffic💰 12 $/Monat fix (mehrere Projekte)
Skalierung📈 Auto-Scaling📊 Ressourcen manuell
Kontrolle⚠️ Plattformregeln✅ Volle Kontrolle
SzenarioEnterprise, globalPersönlich, kleine Teams, knappes Budget

Abschließend:

  • Mehrere Side Projects als Solo-Entwickler – Self-Hosting spart spürbar
  • Kein Ops oder stark schwankender Traffic – Vercel bleibt sinnvoll
  • Kein richtig oder falsch – nur passend oder nicht

Vollständige Configs und Details liegen im GitHub-Repository (Platzhalter – bei Nutzung ersetzen). Fragen gern in den Kommentaren – bekannte Fallstricke sollen niemanden zweimal treffen.

Next.js Docker Self-Hosting – vollständiger Deployment-Ablauf

Von der Standalone-Konfiguration bis zum Go-Live – inklusive Reverse Proxy und Streaming-Fix

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Standalone-Ausgabemodus konfigurieren

    Standalone in next.config.js aktivieren:

    1. next.config.js öffnen
    2. Konfiguration ergänzen: output: 'standalone'
    3. Build ausführen: npm run build
    4. Ausgabe prüfen: Verzeichnis .next/standalone muss existieren

    Wichtig:
    • Standalone kopiert public und .next/static nicht automatisch
    • Beide Verzeichnisse müssen im Dockerfile manuell kopiert werden
    • Sonst liefern statische Assets 404

    Beispiel:
    ```javascript
    const nextConfig = {
    output: 'standalone',
    }
    module.exports = nextConfig
    ```
  2. 2

    Step 2: Mehrstufiges Dockerfile erstellen

    Dockerfile mit drei Build-Phasen:

    Phase 1 – Abhängigkeiten:
    • node:20-alpine als Basis-Image
    • Nur package.json und package-lock.json kopieren
    • npm ci ausführen (Docker-Cache nutzen)

    Phase 2 – App bauen:
    • node_modules aus Phase 1 übernehmen
    • Gesamten Quellcode kopieren
    • npm run build ausführen

    Phase 3 – Produktion:
    • Nicht-Root-Benutzer anlegen (Sicherheit)
    • public kopieren (statische Assets)
    • .next/standalone kopieren
    • .next/static kopieren (CSS/JS-Build-Artefakte)
    • HOSTNAME="0.0.0.0" und PORT=3000 setzen
    • Start: node server.js

    Wichtig:
    • Drei Phasen reduzieren das Image von 1,5 GB auf 200 MB
    • public und .next/static müssen kopiert werden, sonst 404
    • Nicht als root ausführen – Sicherheitsbest Practice
  3. 3

    Step 3: Nginx Reverse Proxy konfigurieren

    Nginx Reverse Proxy einrichten und Buffering deaktivieren:

    1. Nginx installieren (oder Caddy nutzen)
    2. SSL-Zertifikat einrichten (Let's Encrypt)
    3. Nginx-Konfigurationsdatei anlegen

    Pflicht-Einstellungen:
    • proxy_buffering off; (Buffering aus)
    • proxy_cache off; (Cache aus)
    • proxy_set_header X-Accel-Buffering no; (Nginx explizit kein Buffering)

    Erforderliche Header:
    • 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;

    WebSocket (falls nötig):
    • proxy_set_header Upgrade $http_upgrade;
    • proxy_set_header Connection "upgrade";

    Konfiguration testen:
    ```bash
    nginx -t # Syntax prüfen
    nginx -s reload # neu laden
    ```

    Hinweis: Ohne deaktiviertes Buffering funktioniert Streaming-Rendering nicht
  4. 4

    Step 4: Umgebungsvariablen handhaben

    Build-Zeit und Laufzeit unterscheiden:

    Build-Zeit (Präfix NEXT_PUBLIC_):
    • Beim docker build übergeben
    • Mit --build-arg
    • Im Dockerfile: ARG NEXT_PUBLIC_API_URL
    • Als ENV setzen: ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

    Laufzeit (serverseitig):
    • Mit docker run -e übergeben
    • Oder in docker-compose.yml konfigurieren
    • Im Code aus process.env lesen

    Im Standalone-Modus:
    • runtimeConfig funktioniert nicht
    • App-Router-Methode für Umgebungsvariablen nutzen
    • Servercode: const dbUrl = process.env.DATABASE_URL

    Beispiel:
    ```bash
    # Build
    docker build --build-arg NEXT_PUBLIC_API_URL="https://api.example.com" .

    # Laufzeit
    docker run -e DATABASE_URL="postgres://..." your-image
    ```
  5. 5

    Step 5: Streaming-Rendering-Probleme beheben

    Streaming-Ausfall beheben (KI-Chat, SSE usw.):

    Symptome:
    • Kein Streaming – Ausgabe kommt erst spät auf einmal
    • Server-Sent Events pushen nicht in Echtzeit

    Lösung 1 – Nginx (Pflicht):
    • proxy_buffering off gesetzt
    • X-Accel-Buffering: no gesetzt
    • Nginx neu starten

    Lösung 2 – Edge Runtime:
    • In API-Route: export const runtime = 'edge'
    • Edge Runtime ist für Streaming optimiert
    • In Docker oft stabiler

    Fix verifizieren:
    ```bash
    curl -N http://yourdomain.com/api/chat \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"message": "Hello"}'
    ```
    Mit -N (kein Buffering) sollte zeilenweise Ausgabe sichtbar sein

    Weitere Checks:
    • Cloudflare-Proxy: orange Wolke puffert ebenfalls (deaktivieren oder Pro)
    • Docker-Healthcheck kann Streaming stören
    • Load Balancer davor: separat konfigurieren
  6. 6

    Step 6: Deployment und Verifikation

    Image bauen und deployen:

    1. Docker-Image bauen:
    ```bash
    docker build -t nextjs-app .
    ```

    2. Container starten:
    ```bash
    docker run -d \
    -p 3000:3000 \
    -e DATABASE_URL="postgres://..." \
    -e API_KEY="xxx" \
    --name nextjs-app \
    nextjs-app
    ```

    3. Deployment prüfen:
    • Container-Status: docker ps
    • Logs: docker logs nextjs-app
    • Zugriff testen: curl http://localhost:3000
    • Statische Assets: /_next/static/ aufrufen

    4. Nginx konfigurieren und neu starten:
    • Reverse-Proxy korrekt einrichten
    • HTTPS testen
    • Streaming-Rendering prüfen

    5. Monitoring und Wartung:
    • Auto-Restart: --restart unless-stopped
    • Logs regelmäßig prüfen
    • Server-Ressourcen überwachen

    Typische Fehler:
    • Statische Assets 404: public und .next/static im Dockerfile kopiert?
    • Umgebungsvariablen wirkungslos: Build vs. Laufzeit unterscheiden
    • Container nicht erreichbar: HOSTNAME auf 0.0.0.0?

FAQ

Wie viel spart Self-Hosting? Wie sieht der Kostenvergleich aus?
Vercel kostet etwa 35–50 $/Monat (trafficabhängig), Docker-Self-Hosting fix 12 $/Monat (mehrere Projekte möglich). Monatlich 25–40 $ Ersparnis, pro Jahr 300–500 $. Self-Hosting-Kosten bleiben fix – keine Explosion bei Traffic-Spitzen. Ideal für persönliche Projekte, kleine Teams und knappes Budget.
Warum liefern statische Assets 404? Wie beheben?
Im Standalone-Modus kopiert Next.js public und .next/static nicht automatisch. Fix in der runner-Phase des Dockerfiles:
• COPY --from=builder /app/public ./public
• COPY --from=builder /app/.next/static ./.next/static
Bei weiterhin 404: Dateirechte prüfen, --chown=nextjs:nodejs setzen.
Streaming-Rendering funktioniert nicht – was tun?
In 99 % der Fälle Reverse-Proxy-Buffering. Fix:
1) Nginx: proxy_buffering off; proxy_set_header X-Accel-Buffering no;
2) API-Route: export const runtime = 'edge'
3) Cloudflare-Proxy: orange Wolke deaktivieren oder Pro
4) Test mit curl -N – zeilenweise Ausgabe erwartet
Umgebungsvariablen greifen nicht – was tun?
Build vs. Laufzeit trennen:
• NEXT_PUBLIC_*: beim docker build mit --build-arg, ARG und ENV im Dockerfile
• Laufzeitvariablen: docker run -e oder docker-compose.yml, aus process.env lesen
• Im Standalone-Modus funktioniert runtimeConfig nicht – App-Router-Methode nutzen
Docker-Image ist zu groß – was tun?
Mehrstufigen Build nutzen:
• Phase 1: nur Abhängigkeiten (Docker-Cache)
• Phase 2: App bauen
• Phase 3: nur Laufzeit-Dateien (standalone, public, static)
• Image von 1,5 GB auf 200 MB reduzierbar
• node:20-alpine als Basis verkleinert weiter
Wann Self-Hosting, wann Vercel?
Self-Hosting: Linux/Docker-Grundlagen, stabiler Traffic, knappes Budget, manuelles Deployment akzeptabel, persönliche Projekte/kleine Teams.
Vercel: kein Ops-Know-how, stark schwankender Traffic, globales Edge-Netzwerk, Vercel-Features (Analytics, Edge Config), Budget für Entwicklungseffizienz.
Container läuft, ist aber nicht erreichbar – was prüfen?
Folgendes prüfen:
1) HOSTNAME muss 0.0.0.0 sein (nicht 127.0.0.1), im Dockerfile: ENV HOSTNAME="0.0.0.0"
2) Port-Mapping: docker run -p 3000:3000
3) Container läuft: docker ps
4) Logs: docker logs <container-id>
5) Im Container testen: docker exec -it <container-id> wget -O- http://localhost:3000

9 Min. Lesezeit · Veröffentlicht am: 20. Dez. 2025 · Aktualisiert am: 14. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog