Die Bereitstellung moderner Webanwendungen mit Bun, TanStack Start und Prisma auf Plattformen wie Railway kann knifflig sein. Obwohl Railway Nixpacks unterstützt, das Bun-Projekte automatisch erkennt, reicht die Standardkonfiguration für komplexe Anwendungen wie diese nicht aus. Besonders problematisch wird es, wenn Abhängigkeiten wie prisma generate zur Build-Zeit, der Vite-Plugin-TanStack-Start, eine benutzerdefinierte server.ts-Datei und prisma migrate deploy beim Start benötigt werden. Ein manuell erstelltes Dockerfile mit vier Stufen bietet hier eine robustere und zuverlässigere Lösung als das Anpassen von Nixpacks.
Warum Railway und Nixpacks für Bun-Projekte nicht ausreichen
Nixpacks erleichtert zwar die Bereitstellung vieler Anwendungen, stößt jedoch an Grenzen, wenn spezielle Build- und Runtime-Anforderungen kombiniert werden. Bei Projekten mit Bun als Runtime, TanStack Start als Framework und Prisma als ORM müssen mehrere Schritte präzise orchestriert werden:
- Der Prisma-Client muss zur Build-Zeit generiert werden, um die Datenbankabfragen zu ermöglichen.
- Vite benötigt das TanStack-Start-Plugin, um die Anwendung korrekt zu kompilieren.
- Eine benutzerdefinierte
server.ts-Datei dient als Einstiegspunkt für die Serverlogik. - Beim Start der Anwendung müssen Datenbankmigrationen (
prisma migrate deploy) ausgeführt werden, bevor die Serverinstanz startet.
Diese Anforderungen überschreiten die Standardfunktionalität von Nixpacks, das solche komplexen Abhängigkeiten nicht ohne Weiteres handhaben kann. Ein maßgeschneidertes Dockerfile bietet hier die notwendige Kontrolle und Flexibilität.
Schritt-für-Schritt-Anleitung für ein optimiertes Dockerfile
Die Bereitstellung von Bun-Anwendungen mit TanStack Start und Prisma auf Railway erfordert eine sorgfältige Konfiguration des Docker-Images. Das folgende Rezept zeigt, wie die Abhängigkeiten und Build-Prozesse optimal strukturiert werden können:
1. Abhängigkeiten installieren – parallel und effizient
Das Dockerfile beginnt mit zwei parallelen Stufen für die Installation der Abhängigkeiten. Eine Stufe installiert alle Abhängigkeiten inklusive der Entwicklungsabhängigkeiten, während die andere nur die Produktionsabhängigkeiten (deps-prod) installiert. Dies spart Speicherplatz und beschleunigt den Build-Prozess:
# Installiere alle Abhängigkeiten (inkl. Dev-Abhängigkeiten)
FROM bun:latest AS deps-full
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
# Installiere nur Produktionsabhängigkeiten
FROM bun:latest AS deps-prod
WORKDIR /app
COPY --from=deps-full /app/node_modules ./node_modules
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production2. Prisma-Client generieren und Build-Prozess ausführen
In der nächsten Stufe wird der Prisma-Client generiert und die Anwendung kompiliert. Hier wird ein Dummy-Datenbank-URL (DATABASE_URL) verwendet, um sicherzustellen, dass Prisma die Client-Dateien korrekt generiert. Anschließend wird die Anwendung mit Vite und dem TanStack-Start-Plugin gebaut:
FROM deps-full AS builder
WORKDIR /app
COPY --from=deps-full /app/node_modules ./node_modules
# Setze eine Dummy-Datenbank-URL für die Client-Generierung
ENV DATABASE_URL="postgresql://user:pass@dummy-host:5432/db"
# Generiere den Prisma-Client
RUN bun prisma generate
# Baue die Anwendung mit Vite und TanStack Start
COPY . .
RUN bun run build3. Laufzeit-Image erstellen – schlank und effizient
Das finale Image kombiniert die Produktionsabhängigkeiten mit den generierten Dateien aus dem Build-Prozess. Der Prisma-Client wird auf die Produktionsabhängigkeiten (deps-prod) geschichtet, um die Dateigröße zu minimieren. Zudem wird der Einstiegspunkt (server.ts) als Hauptprozess definiert:
FROM deps-prod AS runtime
WORKDIR /app
# Kopiere die Produktionsdateien
COPY --from=builder /app/.output ./.output
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/server.ts ./server.ts
# Lege den Prisma-Client über die Produktionsabhängigkeiten
COPY --from=builder /app/node_modules/@prisma/client ./node_modules/@prisma/client
# Starte den Server
CMD ["bun", "run", "server.ts"]4. Datenbankmigrationen und saubere Prozessbeendigung
Ein weiterer kritischer Punkt ist die Ausführung von Datenbankmigrationen beim Start der Anwendung. Hierfür wird ein Skript verwendet, das prisma migrate deploy ausführt, bevor der Bun-Server gestartet wird. Dies stellt sicher, dass die Datenbankmigrationen abgeschlossen sind, bevor die Anwendung API-Aufrufe entgegennimmt. Zudem wird der Prozess so konfiguriert, dass er auf das SIGTERM-Signal von Railway reagiert und sauber beendet wird:
#!/bin/sh
# Führe Migrationen aus
bun prisma migrate deploy
# Starte den Server
bun run server.tsWichtige Railway-spezifische Einstellungen
Neben der Dockerfile-Konfiguration gibt es einige Railway-spezifische Einstellungen, die für eine erfolgreiche Bereitstellung entscheidend sind:
Postgres-Verbindung konfigurieren
Railway bietet Umgebungsvariablen für Datenbankverbindungen an. Die Postgres-Verbindung sollte explizit referenziert werden, um sicherzustellen, dass die Anwendung die korrekten Zugangsdaten verwendet. Dies erfolgt über die DATABASE_URL-Umgebungsvariable, die von Railway automatisch bereitgestellt wird.
Server explizit an 0.0.0.0 binden
Standardmäßig bindet Bun an localhost, was in einem Containerumfeld zu Problemen führt. Die Anwendung sollte explizit an 0.0.0.0 gebunden werden, um sicherzustellen, dass Railway die Anwendung erreichen kann:
// server.ts
Bun.serve({
fetch: app.fetch,
port: process.env.PORT || 3000,
hostname: "0.0.0.0", // Wichtig für Railway
});COPY-Anordnung im Dockerfile optimieren
Die Reihenfolge der COPY-Anweisungen im Dockerfile beeinflusst, welche Dateien im Image verfügbar sind. Besonders wichtig ist dies für den Prisma-Client, der zur Laufzeit benötigt wird. Die Dateien sollten in der richtigen Reihenfolge kopiert werden, um sicherzustellen, dass der Client verfügbar ist und die Anwendung korrekt funktioniert.
Fazit: Robuste Bereitstellung für anspruchsvolle Projekte
Die Kombination aus Bun, TanStack Start und Prisma bietet eine leistungsstarke Grundlage für moderne Webanwendungen. Die Bereitstellung auf Railway erfordert jedoch eine sorgfältige Konfiguration, um sicherzustellen, dass alle Abhängigkeiten korrekt installiert, der Build-Prozess optimiert und die Laufzeitumgebung stabil ist. Mit einem maßgeschneiderten Dockerfile und den richtigen Railway-Einstellungen lässt sich diese Herausforderung jedoch zuverlässig meistern. Wer ähnliche Anwendungsarchitekturen einsetzt, kann von diesem Rezept profitieren und seine Bereitstellungsprozesse professionalisieren.
KI-Zusammenfassung
Bun çalışma zamanı ve TanStack Start ile geliştirilen projelerinizi Railway’e nasıl dağıtacağınızı adım adım öğrenin. Dört aşamalı Dockerfile rehberiyle otomatik derleme sorunlarını çözün.