12k
All articles

Wie man eine Bun-Anwendung dockerisiert

Dockerize eine Bun-App mit produktionsreifem Dockerfile, .dockerignore, 0.0.0.0-Bindung, Healthchecks, Compose und SIGTERM-Shutdown.

OpenReplay Team
OpenReplay Team
Wie man eine Bun-Anwendung dockerisiert

Ein produktionstaugliches Dockerfile für einen Bun-Service passt in wenige Zeilen, aber der Unterschied zwischen einem Dockerfile, das lokal funktioniert, und einem, das einen Deployment übersteht, hängt von vier Dingen ab: einem gepinnten Base-Image, einem Non-Root-User, einem Service, der an 0.0.0.0 gebunden ist, und einem SIGTERM-Handler, der laufende Anfragen noch zu Ende bearbeitet. Dieser Artikel liefert das fertige Dockerfile und schließt anschließend jede dieser Lücken mit kopierfertigem Code.

Sie haben eine Bun-App – eine API, einen Worker oder ein Backend-for-Frontend – die auf Ihrem Rechner läuft. Sie möchten sie containerisieren, ohne die Docker-Eigenheiten von Bun auf die harte Tour kennenlernen zu müssen. Im Folgenden: das minimale Dockerfile, Layer-Caching, ein Multi-Stage-Build mit kompiliertem Binary, Produktionshärtung, Docker Compose mit Postgres, ein Graceful-Shutdown-Handler für Buns integrierten Server und eine Tabelle der vier häufigsten stillen Fehler mit ihren Lösungen.

Wichtigste Erkenntnisse

  • Ein Bun-Service, der innerhalb eines Containers nur auf 127.0.0.1 lauscht, ist über einen veröffentlichten Port nicht erreichbar; binden Sie ihn an 0.0.0.0 (alle IPv4-Interfaces), damit Dockers Port-Publishing den Traffic weiterleiten kann.
  • Bun 1.2, veröffentlicht am 22. Januar 2025, hat das Standard-Lockfile-Format auf das textbasierte bun.lock umgestellt; migrieren Sie von bun.lockb mit bun install --save-text-lockfile --frozen-lockfile --lockfile-only.
  • Docker-Image-Referenzen verwenden standardmäßig latest, wenn kein Tag angegeben ist, was zwischen Deployments implizit Ihre Bun-Runtime ändern kann; pinnen Sie einen expliziten Tag wie oven/bun:1.
  • Kopieren Sie package.json und bun.lock vor dem Anwendungsquellcode, damit Dockers Layer-Cache bun install bei Rebuilds überspringt, bei denen sich nur der Quellcode geändert hat.
  • Ohne einen SIGTERM-Handler sendet Docker SIGTERM, wartet bis zum Standard-Stop-Timeout (10 Sekunden bei Linux-Containern) und sendet dann SIGKILL, wenn der Prozess noch nicht beendet wurde, was laufende Anfragen abbrechen kann.

Das minimale Dockerfile

Vier Zeilen genügen, um eine Bun-App in einen Container zu bringen. Dies ist der schnellste Weg von „läuft lokal” zu einem fertigen Image.

FROM oven/bun
COPY . .
RUN bun install
CMD ["bun", "run", "src/index.ts"]

Build-Befehl:

docker build -t bun-app .
docker run -p 3000:3000 bun-app

Bun führt TypeScript direkt aus, daher gibt es keinen tsc-Build-Schritt – der Einstiegspunkt ist Ihre .ts-Datei. Ersetzen Sie src/index.ts durch Ihren tatsächlichen Einstiegspunkt. Das Base-Image ist das offizielle oven/bun-Image auf Docker Hub.

Verwenden Sie dieses Dockerfile für Experimente, nicht für die Produktion. Die Zeile FROM oven/bun zieht implizit latest, kopiert Ihr gesamtes Verzeichnis (einschließlich node_modules und .env), läuft als Root und baut Abhängigkeiten bei jeder Code-Änderung neu. Der Rest dieses Artikels behebt jeden dieser Punkte.

Eine .dockerignore-Datei hinzufügen

Eine .dockerignore-Datei hält Ihren Build-Kontext schlank und verhindert, dass Secrets und lokale Artefakte in das Image kopiert werden. Erstellen Sie sie als Erstes.

node_modules
.git
.gitignore
.env
.env.*
dist
*.log
.vscode
README.md

Zwei Einträge sind besonders wichtig. node_modules wird ausgeschlossen, weil bun install das Verzeichnis innerhalb des Images aus Ihrem Lockfile neu generiert – das Kopieren des lokalen node_modules würde plattformspezifische Binärdateien einschließen, die möglicherweise nicht zum Container passen. .env und .env.* werden ausgeschlossen, damit lokale Secrets niemals in einem Image-Layer landen, wo sie selbst dann erhalten bleiben, wenn ein späterer Layer die Datei löscht.

Sollte ich bun.lock oder bun.lockb in meinem Dockerfile verwenden?

Verwenden Sie standardmäßig bun.lock. Bun 1.2, veröffentlicht am 22. Januar 2025, hat das Standard-Lockfile-Format auf das textbasierte bun.lock umgestellt und damit das ältere binäre bun.lockb abgelöst. Ältere Tutorials referenzieren noch bun.lockb, und das Kopieren des falschen Dateinamens in Ihr Dockerfile führt zu einem verwirrenden --frozen-lockfile-Fehler.

Wenn Ihr Repository noch eine binäre Lockfile enthält, migrieren Sie einmalig:

bun install --save-text-lockfile --frozen-lockfile --lockfile-only
# dann bun.lockb löschen und bun.lock committen

Dieser Befehl ist in der Bun-Lockfile-Dokumentation dokumentiert. Referenzieren Sie nach der Migration überall in Ihrem Dockerfile bun.lock.

Layer-Caching: Abhängigkeiten vom Quellcode trennen

Kopieren Sie package.json und bun.lock vor Ihrem Anwendungsquellcode, damit Dockers Layer-Cache bun install bei jedem Rebuild überspringt, bei dem sich nur Quelldateien geändert haben – die einzelne wirkungsvollste Änderung für die lokale Entwicklungsgeschwindigkeit.

FROM oven/bun:1

WORKDIR /app

# Lockfile und Manifest zuerst kopieren — dieser Layer wird nur
# invalidiert, wenn sich Abhängigkeiten ändern
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

# Quellcode zuletzt kopieren — Code-Änderungen bauen nur ab hier neu
COPY src ./src
COPY tsconfig.json ./

CMD ["bun", "run", "src/index.ts"]

Docker cached jede Anweisung als Layer und verwendet gecachte Layer wieder, wenn sich ihre Eingaben nicht geändert haben. Da COPY package.json bun.lock vor COPY src steht, lässt das Bearbeiten einer Quelldatei den Abhängigkeits-Layer unberührt und bun install wird vollständig übersprungen. bun install --frozen-lockfile bricht den Build ab, wenn bun.lock nicht mit package.json synchron ist – das korrekte Verhalten in einem Dockerfile, da es Lockfile-Abweichungen zur Build-Zeit statt zur Laufzeit aufdeckt. Das Verhalten des Flags ist in der Bun-Install-CLI-Dokumentation dokumentiert.

Beachten Sie die Zeile COPY tsconfig.json. Bun liest tsconfig.json für Pfad-Aliase und Compiler-Optionen; wenn Ihre App Path-Mapping verwendet und Sie das Kopieren vergessen, schlägt die Modulauflösung beim Start innerhalb des Containers fehl, obwohl sie lokal funktioniert.

Multi-Stage-Build: Zu einem eigenständigen Binary kompilieren

bun build --compile erstellt eine eigenständige ausführbare Datei, die Ihren Code, npm-Pakete, Assets und die Bun-Runtime bündelt, sodass das finale Image Bun nicht separat installiert haben muss. Verwenden Sie es in einem Multi-Stage-Build, um nur das Binary in ein minimales Base-Image zu kopieren und so ein kleineres finales Image zu erhalten.

# --- Build-Stage ---
FROM oven/bun:1 AS build
WORKDIR /app

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

COPY src ./src
COPY tsconfig.json ./

# Zu einer einzelnen ausführbaren Datei kompilieren, die die Bun-Runtime enthält
RUN bun build ./src/index.ts --compile --outfile server

# --- Runtime-Stage ---
FROM debian:12-slim
WORKDIR /app

COPY --from=build /app/server /app/server

USER nobody
EXPOSE 3000
CMD ["/app/server"]

Die Build-Stage installiert Abhängigkeiten und kompiliert. Die Runtime-Stage startet von debian:12-slim und kopiert nur das kompilierte server-Binary hinein – kein Bun-Image, kein node_modules, kein Quellcode. Das Binary enthält die Runtime und läuft daher ohne eine Bun-Installation. Messen Sie die Größendifferenz für Ihre eigene App, bevor Sie eine Zahl nennen; sie hängt von der Anzahl der Abhängigkeiten und der Asset-Größe ab.

Produktionshärtung

Pinnen Sie einen expliziten Bun-Tag, führen Sie den Prozess als Non-Root-User aus und installieren Sie nur Produktionsabhängigkeiten. Diese drei Änderungen machen aus einem funktionierenden Dockerfile ein deploymentfähiges.

FROM oven/bun:1-alpine

WORKDIR /app

# Einen Non-Root-User anlegen (Alpine-Syntax)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

COPY src ./src
COPY tsconfig.json ./

RUN chown -R appuser:appgroup /app
USER appuser

ENV NODE_ENV=production
EXPOSE 3000

CMD ["bun", "run", "src/index.ts"]

Tag pinnen. Docker-Image-Referenzen verwenden standardmäßig latest, wenn kein Tag angegeben ist, was bedeutet, dass ein docker pull Ihre Bun-Runtime zwischen Deployments implizit ändern kann. Pinnen Sie einen expliziten Bun-Tag, um Runtime-Upgrades bewusst und nachvollziehbar zu gestalten – Buns offizieller Docker-Leitfaden verwendet oven/bun:1. Die 1-alpine-Variante ist auf Docker Hub für einen kleineren Footprint verfügbar; prüfen Sie, ob sie zu Ihren Abhängigkeiten passt, bevor Sie sie einsetzen, da Alpine musl libc statt glibc verwendet.

Als Non-Root ausführen. Der Standard-Container-User ist Root. Das Anlegen von appuser und der Wechsel dazu mit USER appuser begrenzt den Schaden, falls der Prozess kompromittiert wird – eine Grundlage der Docker-Sicherheitsrichtlinien.

Nur Produktionsabhängigkeiten installieren. bun install --frozen-lockfile --production überspringt devDependencies, hält das Image kleiner und reduziert die Angriffsfläche.

Warum ist mein Bun-Container über den veröffentlichten Port nicht erreichbar?

Ein Bun-Service, der innerhalb eines Docker-Containers nur auf 127.0.0.1 lauscht, ist über einen veröffentlichten Port nicht erreichbar. Binden Sie ihn an 0.0.0.0 (alle IPv4-Interfaces), damit Dockers Port-Publishing den Traffic weiterleiten kann.

Dies ist der häufigste stille Fehler in einem dockerisierten Bun-Service. Der Container startet, die Logs sehen gesund aus, docker ps zeigt den gemappten Port – und jede Anfrage vom Host hängt oder gibt einen Verbindungsfehler zurück. Die Ursache ist die Listen-Adresse.

Für reines Bun.serve() setzen Sie hostname explizit:

// src/index.ts
const port = Number(process.env.PORT) || 3000;

Bun.serve({
  port,
  hostname: "0.0.0.0", // innerhalb von Docker erforderlich
  fetch(req) {
    return new Response(JSON.stringify({ status: "ok" }), {
      headers: { "Content-Type": "application/json" },
    });
  },
});

console.log(`Listening on http://0.0.0.0:${port}`);

Die Bun.serve()-Optionen sind in der Bun-HTTP-API-Dokumentation dokumentiert. Für Elysia konfigurieren Sie serve.hostname: "0.0.0.0". Für andere Frameworks, die auf Bun laufen, stellen Sie sicher, dass der zugrundeliegende Bun-Server so konfiguriert ist, dass er auf 0.0.0.0 lauscht.

Session-Replays von Frontends, die mit einem Bun-Service kommunizieren, können diesen Fehler eine Ebene tiefer im Dockerfile sichtbar machen. Ein Service, der an localhost gebunden ist, äußert sich im Browser als eine Kaskade von fetch/XHR-Timeouts unmittelbar nach einem Deployment – ein Symptom, das wie ein Frontend-Fehler aussieht, aber im Listen-Interface des Containers seinen Ursprung hat. Tools wie OpenReplay erfassen dieses clientseitige Fehlermuster, wo diese Fehlkonfiguration in der Regel zuerst bemerkt wird.

Docker Compose mit Postgres

Eine Docker-Compose-Datei führt Ihre Bun-App zusammen mit PostgreSQL aus, wartet auf einen erfolgreichen Healthcheck der Datenbank, bevor die App gestartet wird, und startet die App bei einem Absturz neu.

# compose.yaml
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    env_file:
      - .env.production
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pgdata:

depends_on mit condition: service_healthy hält den api-Service zurück, bis Postgres als gesund gemeldet wird, was einen Healthcheck auf der Abhängigkeit erfordert – ohne einen solchen wird service_healthy nie aufgelöst. restart: unless-stopped startet die App nach einem Absturz neu, respektiert aber einen manuellen Stopp.

Beachten Sie, dass kein version:-Schlüssel auf oberster Ebene vorhanden ist. Die Compose-Spezifikation markiert die version-Eigenschaft auf oberster Ebene als veraltet; Compose ignoriert sie bei der Schema-Auswahl und gibt eine Warnung aus, wenn sie vorhanden ist. Lassen Sie sie weg.

Umgebungsvariablen: Secrets nicht ins Image einbacken

Übergeben Sie Konfiguration zur Laufzeit über env_file, anstatt Secrets in einen Image-Layer zu kopieren. Image-Layer sind persistent und inspizierbar, sodass ein einmal hineingeschriebenes Secret auch dann wiederherstellbar bleibt, wenn eine spätere Anweisung es löscht.

Der Compose-Service oben referenziert bereits .env.production über env_file. Lesen Sie diese Werte im Code aus process.env:

// src/config.ts
export const config = {
  port: Number(process.env.PORT) || 3000,
  databaseUrl: process.env.DATABASE_URL ?? "",
  jwtSecret: process.env.JWT_SECRET ?? "",
};

Halten Sie .env.production mit dem Eintrag .env.* in .dockerignore aus dem Image heraus und mit einem entsprechenden .gitignore-Eintrag aus der Versionskontrolle. Das Image bleibt generisch; Secrets werden zur Laufzeit injiziert.

Entwicklungs-Workflow: Hot Reload mit einem Bind-Mount

Für die lokale Entwicklung verwenden Sie ein separates Dockerfile, das bun --hot ausführt, und mounten Ihren Quellcode als Volume, damit Änderungen sofort innerhalb des Containers neu geladen werden. Halten Sie dies von Ihrem Produktions-Dockerfile getrennt.

# Dockerfile.dev
FROM oven/bun:1-alpine
WORKDIR /app

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

COPY . .
EXPOSE 3000

CMD ["bun", "--hot", "src/index.ts"]
# compose.dev.yaml
services:
  api-dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    env_file:
      - .env.development

Das --hot-Flag aktiviert Buns Hot Reloading: Das Speichern einer Datei aktualisiert den laufenden Server ohne einen vollständigen Prozessneustart. Der Bind-Mount .:/app macht Host-Änderungen innerhalb des Containers sichtbar, und das anonyme /app/node_modules-Volume verhindert, dass das Host-Verzeichnis die während des Builds installierten Abhängigkeiten überlagert. Starten Sie es mit docker compose -f compose.dev.yaml up --build.

Graceful Shutdown: SIGTERM behandeln

Fügen Sie einen SIGTERM-Handler hinzu, damit laufende Anfragen abgeschlossen werden, bevor der Prozess beendet wird. Ohne einen solchen sendet Docker SIGTERM, wartet bis zum Standard-Stop-Timeout (10 Sekunden bei Linux-Containern) und sendet dann SIGKILL, wenn der Prozess noch nicht beendet wurde, was laufende Anfragen abbrechen kann.

Bun.serve() gibt ein Server-Objekt mit einer .stop()-Methode zurück. Warten Sie auf diese bei SIGTERM, damit laufende Anfragen abgeschlossen werden, bevor der Prozess beendet wird:

// src/index.ts
const server = Bun.serve({
  port: Number(process.env.PORT) || 3000,
  hostname: "0.0.0.0",
  fetch() {
    return new Response("ok");
  },
});

const shutdown = async (signal: string) => {
  console.log(`Received ${signal}, draining connections`);
  await server.stop();
  process.exit(0);
};

process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));

Das server-Handle und seine .stop()-Methode sind Teil der Bun-HTTP-API. Das grundlegende Muster besteht darin, die von Bun.serve() zurückgegebene Server-Instanz zu erfassen und sie beim Empfang eines Beendigungssignals zu stoppen.

Erwartete Image-Größen

Das gewählte Base-Image bestimmt den Großteil der finalen Image-Größe. Docker Hub veröffentlicht komprimierte Größen für jede oven/bun-Variante; die folgenden Werte stammen von der Docker-Hub-Tags-Seite für oven/bun:1.3.14 (linux/amd64).

Base-ImageVarianteKomprimierte Größe
oven/bun:1.3.14Debian-basiert81,93 MB
oven/bun:1.3.14-slimDebian Slim63,32 MB
oven/bun:1.3.14-alpineAlpine40,87 MB
oven/bun:1.3.14-distrolessDistroless40,52 MB

Dies sind Base-Image-Größen, nicht Ihre finale App-Image-Größe. Ihr Image fügt Abhängigkeiten und Quellcode hinzu. Um Ihre eigene Größe zu messen, führen Sie aus:

docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"

Die kompilierte-Binary-Multi-Stage-Variante auf debian:12-slim kann das finale Image weiter verkleinern, da sie das vollständige Bun-Image und node_modules weglässt, aber die genaue Differenz hängt von Ihrem Abhängigkeits-Footprint ab – messen Sie es, anstatt einen generischen Prozentwert zu nennen.

Was sind die häufigsten Fehler bei Bun-Docker-Containern?

Die vier häufigsten Gründe, warum ein dockerisierter Bun-Container still versagt, sind ein falscher Einstiegspunkt-Pfad, localhost-Bindung, eine veraltete Lockfile und ein fehlender tsconfig.json-Kopierschritt. Jeder hat ein eindeutiges Symptom und eine einzeilige Lösung.

SymptomUrsacheLösung
Container beendet sich sofort, Code 1Falscher Einstiegspunkt-Pfad in CMD oder Einstiegsdatei nicht kopiertÜberprüfen Sie, ob der Pfad in CMD ["bun", "run", "src/index.ts"] mit der kopierten Datei übereinstimmt; prüfen Sie, ob COPY src ./src ausgeführt wurde
Port gemappt, aber nicht erreichbarService an 127.0.0.1/localhost gebundenAn 0.0.0.0 binden mit Bun.serve({ hostname: "0.0.0.0" })
Build bricht während bun install abbun.lock nicht synchron mit package.jsonbun install lokal ausführen, aktualisiertes bun.lock committen; --frozen-lockfile funktioniert dann
Modulauflösung schlägt beim Start fehltsconfig.json-Pfad-Aliase nicht aufgelöst – Datei nicht kopiertCOPY tsconfig.json ./ vor dem CMD hinzufügen

Wenn ein Container beendet wird, bevor Sie ihn inspizieren können, öffnen Sie eine Shell im Image, um zu prüfen, was tatsächlich kopiert wurde:

docker run -it --rm --entrypoint sh bun-app
# darin: ls /app, cat package.json, usw.

Der Lockfile-Mismatch-Fall wird am häufigsten fälschlicherweise als Docker-Problem interpretiert. Es ist ein Bun-Problem, das durch Docker sichtbar wird – bun install --frozen-lockfile tut genau das Richtige, indem es sich weigert, gegen eine abweichende Lockfile zu bauen, genau wie in der Bun-Install-Dokumentation beschrieben.

Fazit

Sie haben jetzt alle Bausteine: ein gepinntes, Non-Root-Produktions-Dockerfile; eine Multi-Stage-Variante mit kompiliertem Binary; einen Compose-Stack mit einem healthgechecked Postgres; Hot-Reload-Entwicklungswerkzeuge; und einen SIGTERM-Handler, der Anfragen sauber zu Ende bearbeitet. Beginnen Sie damit, oven/bun durch oven/bun:1 zu ersetzen und die 0.0.0.0-Bindung hinzuzufügen – diese beiden Änderungen beseitigen die zwei Fehler, die beim ersten Deployment am wahrscheinlichsten auftreten, und fügen Sie den Rest hinzu, wenn Ihr Service wächst.

FAQs

Benötige ich einen separaten Build-Schritt, um TypeScript in einem Bun-Docker-Container auszuführen?

Nein. Bun führt TypeScript direkt aus, daher gibt es keinen tsc- oder tsx-Kompilierungsschritt im Docker-Build. Ihr CMD zeigt direkt auf die .ts-Einstiegsdatei, zum Beispiel CMD ['bun', 'run', 'src/index.ts']. Der einzige Vorbehalt betrifft Pfad-Aliase: Wenn Ihre App tsconfig.json-Path-Mapping verwendet, müssen Sie tsconfig.json in das Image kopieren, sonst schlägt die Modulauflösung beim Start fehl, obwohl sie lokal funktioniert.

Wann sollte ich ein kompiliertes Binary mit bun build --compile statt bun run im Container verwenden?

Verwenden Sie den Multi-Stage-Build mit kompiliertem Binary, wenn Sie ein kleineres finales Image und keine Bun-Installation in der Runtime-Stage wünschen. bun build --compile erzeugt eine eigenständige ausführbare Datei, die Ihren Code, npm-Pakete, Assets und die Bun-Runtime bündelt, sodass Sie nur dieses Binary auf ein minimales Base-Image wie debian:12-slim kopieren können – ohne node_modules oder Bun-Image. Verwenden Sie einfaches bun run für einfachere Builds oder wenn Sie Runtime-Features wie Hot Reload während der Entwicklung benötigen.

Funktioniert die Alpine-Variante des oven/bun-Images mit jeder Abhängigkeit?

Nicht immer. Die Variante oven/bun:1-alpine verwendet musl libc statt glibc, während das Standard-Debian-basierte oven/bun:1-Image glibc verwendet. Native Abhängigkeiten, die gegen glibc kompiliert wurden, oder Pakete, die nur glibc-vorkompilierte Binärdateien bereitstellen, können auf Alpine fehlschlagen. Das Alpine-Image ist kleiner, aber überprüfen Sie, ob Ihr Abhängigkeitsbaum dort gebaut wird und läuft, bevor Sie es in der Produktion einsetzen, anstatt einen direkten Austausch anzunehmen.

Warum schlägt mein Container-Build während bun install mit einem frozen-lockfile-Fehler fehl?

Der Build bricht ab, weil bun.lock nicht mit package.json synchron ist und bun install --frozen-lockfile sich weigert, gegen eine abweichende Lockfile zu bauen. Dies ist das korrekte Verhalten in einem Dockerfile: Es deckt Lockfile-Abweichungen zur Build-Zeit statt zur Laufzeit auf. Beheben Sie es, indem Sie bun install lokal ausführen, um bun.lock neu zu generieren, und dann die aktualisierte Datei committen. Wenn Ihr Dockerfile noch das ältere binäre bun.lockb referenziert, wechseln Sie zu bun.lock, da Bun 1.2 das textbasierte Format zum Standard gemacht hat.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.