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`;
});
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();
| 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);
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");
},
});
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"
}
}
// packages/web/package.json — catalog reference
{
"name": "@manoit/web",
"dependencies": {
"react": "catalog:",
"zod": "catalog:"
}
}
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)