DEV Community

daniel jeong
daniel jeong

Posted on • Originally published at manoit.co.kr

Bun 1.3 + Anthropic Rust Rewrite — Bun.SQL, Bun.Image, HTTP/3 QUIC, and a 6-Day 960K-Line Zig Rust Port

Bun 1.3 + Anthropic Rust Rewrite Deep Dive — Bun.SQL Unified DB Client, Bun.Image, HTTP/3 QUIC, and the 960K-Line Zig→Rust Six-Day Port Redefining the 2026 Full-Stack JavaScript Runtime Standard

On May 14, 2026, the Bun team — now part of Anthropic — merged a pull request that ported roughly 960,000 lines of Zig code to Rust. Work began on May 4. Coding took six days. The PR landed ten days later. The Rust side is 681,000 lines, passes 99.8% of the existing test suite on Linux x64 glibc, leaves about 13,000 unsafe blocks behind, and shrinks the binary by 3–8 MB. Four months earlier, on January 6, Bun 1.3 went GA — Bun.SQL unified PostgreSQL, MySQL, MariaDB, and SQLite behind a single API; the built-in Redis client posted 7.9× the throughput of ioredis; isolated installs and dependency catalogs reshaped the monorepo model; and the Security Scanner API moved supply-chain blocks to install time. The April 22 (1.3.13) and May 6 (1.3.14) point releases — the last Zig builds — added Bun.Image (a Sharp drop-in) and experimental HTTP/3 (QUIC) support, paving the bridge to the next quarter's Rust line. This article breaks down those five months of change across eight axes, with the migration checklist ManoIT used on its BFFs, monorepo, and image pipeline. The audience is the backend/database/API operator deciding where Bun fits on top of the Node 24 LTS standard — not whether to replace it wholesale.

1. Why May 2026 Is Bun's Inflection — Full-Stack Runtime, AI Acquisition, and Rust Port All Land Together

Bun launched in 2022 as the "single-binary runtime that's 4× faster than Node," but through 2025 the operator's verdict was consistent: promising, but the edges were too rough for full-stack production. Three events in the first half of 2026 shifted that verdict simultaneously. (1) Bun 1.3 elevated Bun.SQL, Redis, HMR, image processing, and the Security Scanner into the standard library, anchoring Bun as a full-stack runtime. (2) The Anthropic acquisition announced on December 2, 2025 gave the team funding, headcount, and AI-agent infrastructure. (3) The Zig→Rust port merged on May 14, 2026 answered long-running questions about compiler longevity, ecosystem reach, and licensing alignment.

Date Event Operational meaning
2022.07 Bun 0.1 (Zig) First credible "Node alternative" signal
2024.01 Bun 1.0 GA Stable ABI, ~90% npm compatibility
2025.07 Bun 1.2.19 — isolated installs beta Monorepo isolation surface
2025.12.02 Anthropic acquires Bun Adopted as the Claude Code / Agent SDK runtime
2026.01.06 Bun 1.3 GA Bun.SQL · Redis · Scanner · Catalogs · Isolated default
2026.04.22 Bun 1.3.13 Bun.Image introduced; JPEG/PNG/WebP/GIF/BMP statically linked
2026.05.06 Bun 1.3.14 — last Zig build Experimental HTTP/3 QUIC, global virtual store
2026.05.04–14 Zig→Rust port PR merged 960k lines in 6 days, 99.8% test pass, binary −3–8 MB

Two rows are decisive for operators: 2026.01.06 standardized the libraries needed to run a backend on Bun, and 2026.05.14 raised confidence that this runtime will still be alive in five years. Those two signals arrived together — and that's why ManoIT decided to migrate one of four in-house BFFs (image transform / thumbnail / CDN sidecar) to Bun (see §8).

2. Bun.SQL — One API for PostgreSQL, MySQL, MariaDB, and SQLite

The first big change in Bun 1.3 is that Bun.SQL evolved from a PostgreSQL-only client into a unified multi-driver API. The same object interface drives PostgreSQL, MySQL, MariaDB, and SQLite — with zero external dependencies. Driver selection comes from the connection string scheme.

// Same API, four databases
import { SQL } from "bun";

const pg     = new SQL("postgres://localhost/app");
const mysql  = new SQL("mysql://localhost/app");
const maria  = new SQL("mysql://app:secret@maria.internal:3306/app");
const sqlite = new SQL("sqlite://./local.db");

// Tagged template literal — automatic parameter binding (SQLi-safe)
const userId = 42;
const rows = await pg`SELECT id, email FROM users WHERE id = ${userId}`;

// PostgreSQL arrays — sql.array helper for explicit type cast
const tags = SQL.array(["devops", "kubernetes"], "text");
await pg`INSERT INTO posts(title, tags) VALUES (${"Hello"}, ${tags})`;

// Transactions — async block with auto commit/rollback
await pg.begin(async (tx) => {
  await tx`UPDATE accounts SET balance = balance - 100 WHERE id = 1`;
  await tx`UPDATE accounts SET balance = balance + 100 WHERE id = 2`;
});
Enter fullscreen mode Exit fullscreen mode

Two operational consequences matter most. (1) Zero driver dependencies — no more pg, mysql2, or better-sqlite3 from npm. The supply-chain surface shrinks, the container image's native-binding build step disappears, and lockfile drift between nodes goes away. (2) Tagged-template parameter binding by default${...} is auto-converted to a prepared-statement parameter, so the temptation to hand-concatenate SQL evaporates. The strongest defense against SQL injection is making the unsafe path more awkward to write than the safe one, and Bun.SQL's default API is shaped that way.

2.1 Migration Notes vs. postgres.js · pg · mysql2

Item node-postgres (pg) postgres.js Bun.SQL
Install npm dep npm dep runtime built-in
API style imperative pool.query() tagged template tagged template
Prepared stmts manual auto auto
Connection pool pg-pool built-in built-in
JSON manual auto auto
Arrays manual cast limited SQL.array(value, type)
Multi-DB no postgres only postgres · mysql · maria · sqlite
Streaming cursor object generator async iterable

Migrating from postgres.js to Bun.SQL is roughly a one-line import change. From pg, the API style itself changes. The safe gradual order: (1) write new modules in Bun.SQL, (2) wrap legacy pg calls in a thin abstraction so only the wrapper changes, (3) leave heavy prepared-statement modules for last.

3. Built-in Redis Client — What 7.9× over ioredis Actually Means

Bun 1.3 puts a Redis client into the standard library. It supports 66 commands across STRING · HASH · LIST · SET · SORTED SET · STREAM · PUBSUB · SCRIPTING and posts 7.9× the throughput of ioredis — a combination of native fast paths, a tuned RESP3 parser, and zero-allocation hot paths.

import { redis } from "bun";

// Default client — auto-uses REDIS_URL
await redis.set("session:abc", JSON.stringify({ uid: 42 }), "EX", 3600);
const raw = await redis.get("session:abc");

// Hash and list
await redis.hset("user:42", { name: "Jeong Sin", role: "engineer" });
const profile = await redis.hgetall("user:42");

await redis.lpush("queue:jobs", "job-001", "job-002");
const job = await redis.brpop("queue:jobs", 5);

// Pipeline — single round-trip
const pipe = redis.pipeline();
pipe.incr("counter:requests");
pipe.expire("counter:requests", 60);
const [count, ttlOk] = await pipe.exec();
Enter fullscreen mode Exit fullscreen mode
Metric ioredis 5.4 node-redis 4.7 Bun.redis 1.3 Note
SET throughput (req/s) 1.0× baseline ~1.4× ~7.9× Bun's own bench
P99 latency baseline substantially lower native RESP3 parser
External deps npm package npm package 0 runtime built-in
TLS yes yes yes parity
Cluster yes yes progressively after 1.3.x needs separate discovery
Sentinel yes yes limited constraints today

Watch the last two rows. Cluster and Sentinel parity is not complete at 1.3 GA. Single-node Redis (session store, cache, simple queue) ports to Bun.redis cleanly. ElastiCache / Memorystore cluster-mode workloads need a separate compatibility audit on the current 1.3.x line. ManoIT moved one of four BFFs (single-node Redis) to Bun.redis first and kept ioredis for the cluster-mode ones.

4. Bun.Image — A Sharp Drop-in with Statically Linked Codecs

Bun 1.3.13 (April 22, 2026) introduced Bun.Image, a built-in image processing API. The design intent is explicit — a drop-in replacement for Sharp. Sharp is the de-facto Node standard, but (1) the libvips native binding, (2) per-platform prebuilt binaries, and (3) Apple Silicon / musl / Alpine / Lambda-Layer compatibility regressions are recurring operational headaches. Bun.Image takes the problem head-on.

Format Code path Platform Determinism
JPEG · PNG · WebP · GIF · BMP Statically linked codecs All platforms Byte-identical output
HEIC · AVIF · TIFF OS system backend (lazy load) macOS (ImageIO + vImage) · Windows (WIC) Platform-dependent
SVG Limited (no rasterizer) Use a separate tool
import { Image } from "bun";

// Resize + WebP transcode
const out = await new Image("./photo.jpg")
  .resize({ width: 1280, fit: "inside" })
  .toFormat("webp", { quality: 82 })
  .bytes();

await Bun.write("./photo-1280.webp", out);

// thumbhash placeholder — 12-byte preview payload
const placeholder = await new Image("./photo.jpg").placeholder();

// metadata — sync-on-main; everything else off-thread
const meta = await new Image("./photo.jpg").metadata();
console.log(meta.width, meta.height, meta.format, meta.exif);
Enter fullscreen mode Exit fullscreen mode

Two operational wins matter most: (1) Everything except metadata() runs off the main thread — event-loop blocking disappears. (2) JPEG / PNG / WebP / GIF / BMP produce byte-identical output across all platforms. Pipelines that key CDN cache on image hash used to lose determinism whenever Sharp's libvips version drifted; Bun.Image's statically linked codecs close that gap. The downside is also explicit — HEIC / AVIF / TIFF depend on OS backends, which means effectively unavailable in Linux containers. iPhone-photo (HEIC) ingestion needs a separate transcode stage.

5. HTTP/3 (QUIC), Zero-Config Frontend, Fast Refresh — The Last Pieces of a Full-Stack Runtime

Bun 1.3.14's experimental HTTP/3 (QUIC) support is not a small change. Existing Bun.serve() fetch handlers and routes work unmodified across HTTP/1.1, HTTP/2, and HTTP/3. HTTP/1.1 and HTTP/2 responses automatically include Alt-Svc: h3=":<port>"; ma=86400, so browsers discover the QUIC endpoint without operator intervention.

// Same handler serves h1 / h2 / h3
Bun.serve({
  port: 443,
  tls: { cert: Bun.file("cert.pem"), key: Bun.file("key.pem") },
  http3: true, // experimental — auto-publishes QUIC endpoint
  fetch(req) {
    return new Response("hello over h1/h2/h3");
  },
});
Enter fullscreen mode Exit fullscreen mode

On a Linux x64 single-process loopback, HTTP/3 is meaningfully faster than HTTPS/1.1 from the same server instance, with about 50% of HTTP/3 CPU time spent inside lsquic. Real-world gains are larger on mobile and high-latency networks (0-RTT resumption, multiplex without head-of-line blocking). Operational guidance: turn it on at the edge BFF first. Internal mesh hops are happy with HTTP/2; the public-facing BFF is where the gain lands.

On the frontend side, Bun now accepts an HTML file directly as an entry, auto-transpiling and bundling JS · CSS · React, and HMR + React Fast Refresh work with zero config. This is gradually absorbing Vite's and webpack's territory — but operators should know that zero-config does not 100% replace the Vite plugin ecosystem. Tailwind, MDX, and SVG-as-component patterns still need a plugin or a build post-step.

6. Package Manager — Catalogs, Isolated Installs, Security Scanner, bun why

Bun 1.3 reshaped the package manager more aggressively than any prior release. Four things changed at once.

Feature Pre-1.3 1.3 GA Operational meaning
Install mode hoisted default workspaces default to isolated blocks undeclared dependency access in monorepos
Version catalog none (per-workspace) workspaces.catalog central version pinning
Update tool bun update bun update --interactive selective updates
Dependency tracing none bun why <pkg> explains why a package was installed
Security scan external tool Security Scanner API block at install time
// package.json  workspaces.catalog
{
  "name": "manoit-monorepo",
  "workspaces": ["packages/*"],
  "workspaces.catalog": {
    "react": "^19.1.0",
    "typescript": "^5.7.2",
    "zod": "^3.24.1"
  },
  "scripts": {
    "scan": "bun install --security-scanner=@socketsecurity/bun-security-scanner"
  }
}
Enter fullscreen mode Exit fullscreen mode
// packages/web/package.json  catalog reference
{
  "name": "@manoit/web",
  "dependencies": {
    "react": "catalog:",
    "zod":   "catalog:"
  }
}
Enter fullscreen mode Exit fullscreen mode

The point of isolated installs is to ensure each package can import only what it declared. Hoisted mode let any package accidentally require a sibling's transitive dep that happened to land in the root node_modules, which is the classic "phantom dependency" failure mode of monorepos. Isolated installs block that — similar to pnpm's strict mode. Note: at 1.3.0 GA there was a known bug where catalogs combined with isolated installs failed to deduplicate compatible semver ranges, installing the same package multiple times (GitHub issue #23615). The 1.3.5–1.3.10 patch line stabilized this. Production adoption: 1.3.10+.

The Security Scanner API is a hook that lets external scanners run during bun install. Socket's official integration (@socketsecurity/bun-security-scanner) is the first widely adopted example — it detects malicious packages, typosquatting, and suspicious behavior before writing to disk. In non-interactive (CI) environments, a warn or fatal advisory fails the install, pulling supply-chain blocking earlier in the lifecycle. ManoIT pinned this scanner into the standard bun install profile across the in-house monorepo.

7. Anthropic's Zig→Rust Port — What 6 Days and 960K Lines Actually Say

The May 14 merge is not just a language change. Five signals arrived together.

Layer Observation Meaning
① Speed 960k lines ported in 6 days First-of-its-kind AI-assisted systems-programming refactor at scale
② Verification 99.8% test pass on Linux x64 glibc Semantic preservation in core paths is measured, not asserted
③ Safety ~13,000 unsafe blocks remain "Beginning, not end" — cleanup will roll across multiple quarters
④ License / ecosystem Avoids Zig's no-AI policy Restores upstream-mergeable contribution flow
⑤ Binary / ops −3–8 MB binary size + some memory leaks fixed Container image and cold-start improvements

Operators should look hardest at ③ and ⑤. 13,000 unsafe blocks means moving to Rust is not by itself a memory-safety guarantee. Subsequent quarters will progressively replace those blocks with safe interfaces, and that work will likely keep release-note "fix" sections fuller than usual. The operational rule of thumb is straightforward: wait for the late patch line of the first two minor releases after the port (e.g., 1.4.5+) before promoting to production. Point ⑤ — binary shrink — is meaningful for image size and cold start, especially on AWS Lambda and Cloud Run.

7.1 What "AI ported 960K lines in 6 days" Actually Tells the Industry

The story is not "AI replaced engineers." It is closer to: teams with mature AI agent infrastructure can now do large systems-programming refactors on a new time scale. The preconditions are concrete: (1) a test suite trustworthy at the 99.8% level, (2) a source language structured for largely 1:1 semantics-preserving translation, (3) a CI capable of automatic rollback and retry on the path to merge, and (4) a single decision-maker with the authority to merge with 13,000 unsafe blocks still present (Bun's case: Sumner). To reproduce this velocity, an operating org needs all four — and (1) is usually the gating barrier.

8. ManoIT Validation — Where Did We Plug Bun In? (Migration Checklist)

Over April–May, ManoIT progressively adopted Bun 1.3 across 4 in-house BFFs, 1 monorepo, and 1 image worker pipeline. The decision sheet:

Workload Current runtime Bun adoption Rationale
BFF #1 — User GraphQL Node 24 + Apollo Pending Verify Apollo Server 4 + ESM compatibility first
BFF #2 — Payment callback Node 24 + Fastify Pending Sentinel Redis dependency — Bun.redis Sentinel still maturing
BFF #3 — Image / thumbnail Node 24 + Sharp Migrated Bun.Image determinism, off-thread, smaller binary
BFF #4 — Push notifications Node 24 + ioredis Wait for 1.3.10+ Single-node Redis only — Bun.redis 7.9× attractive
Monorepo — packages/* pnpm 9 Gradual trial catalog + isolated, phantom-dep cleanup
Image worker — Lambda Node 22 + Sharp Migrated Cold start, binary −8 MB, platform determinism
Edge router Node 24 + Fastify After HTTP/3 ships in 1.4.x Wait for QUIC stability
Phase Action Done signal
Day 0–3 (1) Inventory candidate workloads (runtime, libs, DB, cache). (2) Build a compatibility matrix. Pending / Migrate / Skip classification complete
Day 4–10 (3) Add an import alias for the chosen workload. (4) Run both Bun and Node builds on the same CI. Both builds pass unit + integration tests
Day 11–20 (5) Canary 5% → 25% → 100% P50 / P99 latency, memory, cold start ≥ baseline
Day 21+ (6) Drop the Node build; Bun is the single build Image, docs, onboarding guides updated
Quarterly (7) Wait for late patch-line releases before bumping prod versions Release-note fix list stabilized
Quarterly (8) Phantom-dependency cleanup (isolated installs) bun install --frozen-lockfile CI 100% green

Two lessons stood out. (1) The phase where "both Bun and Node builds run on the same CI" was decisive. Unit tests miss subtle ESM / CJS path divergences, missing native bindings that fall back at runtime, and async-hooks behavioral nits — all of which surface in integration tests. (2) Production adoption was held to 1.3.10+ because the catalog × isolated bug from 1.3.0 GA (#23615) and a 1.3.5 lockfile migration issue cleared up only across the patch line. The same principle will apply to the post-port 1.4.x series (after May 14): wait for the late patch line.

9. Closing — Where to Plug Bun In On Top Of Node 24 LTS

In the first half of 2026, the JavaScript backend default for serious teams is Node 24 LTS. Native TypeScript, Explicit Resource Management, OpenSSL 3.5 (PQC), npm 11, the built-in Test Runner — all already in place. Bun won't (and shouldn't) replace that wholesale; the org and library inertia is too large. But specific workloads — image pipelines, single-node-Redis BFFs, cold-start-sensitive Lambda / Cloud Run functions, and monorepos that need phantom-dependency cleanup — get clear gains from Bun 1.3 in operational simplicity, binary size, determinism, and throughput. The pattern is "plug it into the right slots, then re-evaluate after 1.4.x stabilizes," not "migrate everything." Anthropic's acquisition and the Rust port together raise the confidence floor that this runtime will still be alive in five years. ManoIT's adoption of Bun in 2 of 17 microservices this quarter was the first operational validation of that confidence. Whether the next quarter's adoption widens depends on how quickly the 1.4.x patch line stabilizes.


This article was produced by the ManoIT automated blog pipeline cross-referencing the official Bun blog (bun.com/blog) for Bun 1.3, 1.3.13, and 1.3.14 release notes; the official Bun docs (SQL, Catalogs, Security Scanner API); InfoQ "Bun Introduces Built-in Database Clients and Zero-Config Frontend Development"; heise online "Bun 1.3 Becomes Full-Stack JavaScript Runtime"; The Register "Anthropic's Bun Rust rewrite merged at speed of AI" (2026-05-14); DevClass "Bun team trials port from Zig to Rust" (2026-05-11); GitHub oven-sh/bun release tracker and issues (#23615 catalog × isolated, #16880 postgres driver); and Socket.dev "Socket Integrates With Bun 1.3's Security Scanner API" plus the SocketDev/bun-security-scanner repository. Drafted by Anthropic Claude (Opus 4.6); edited and technically reviewed by ManoIT.


Originally published at ManoIT Tech Blog.

Top comments (0)