Bun vs Node.js 本番環境 2026: 2週間の移行実験で分かったこと

うちのチームは4人で、BtoBのSaaSプロダクトを運営している。バックエンドのAPIはずっとNode.js 20 LTSで動かしていて、Bunの存在は2年以上前から知っていたけど「まだ本番には早い」という印象が拭えなくて様子見してた。

転機になったのは去年末。マイクロサービス化を少しずつ進めていて、コンテナのコールドスタートが地味に問題になってきた。個別には130〜180ms程度だけど、それが連鎖するとユーザーに見える形でレイテンシに響いてくる。それで「Bun 1.3、2週間だけ本番相当の環境で検証してみよう」という話になった。

この記事は、その2週間で分かったことをそのまま書いたもの。


ベンチマークの数字: コンテキストなしには意味がない

まず実測値から出しておく。テスト環境はAWS t3.medium、Amazon Linux 2023。対象は社内の軽量なREST API(依存パッケージ数 約40)をベースに作ったテスト用サービス。

コールドスタート(/healthへの初回応答まで)
– Node.js 22.x LTS: 平均 171ms
– Bun 1.3: 平均 29ms

メモリ使用量(アイドル時)
– Node.js: 〜47MB
– Bun: 〜20MB

スループット(wrk、12スレッド、400コネクション、30秒)
– Node.js + Express: 〜27,800 req/s
– Bun + Hono: 〜62,400 req/s

数字だけ見ると「全部Bunに移行しよう」ってなるんだけど、一個重要なことがある。スループットの比較でBun + Honoを使っているのは、「同じものを速くした」のではなく、別のスタックに乗り換えているんだよね。Honoはとても速いフレームワークだけど、Expressのミドルウェアエコシステムとは別物。ここを混同すると移行コストを見誤る。

実際のHono + Bun最小構成はこんな感じ。TypeScriptをトランスパイルなしで動かせるのは、地味に体験として良かった。

// hono + bun: 最小構成(bun run src/index.ts で直接動く)
import { Hono } from 'hono'
import { logger } from 'hono/logger'

const app = new Hono()

app.use('*', logger())

app.get('/health', (c) => c.json({ status: 'ok', runtime: 'bun' }))

app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  // Bunのビルトインsqliteが使える場合はここで直接クエリ
  const user = await db.query('SELECT * FROM users WHERE id = ?', [id])
  return c.json(user)
})

export default app
// bun --hot src/index.ts でホットリロードも動く

ただ、この構成で「既存のExpressベースのコードをそのまま動かそう」とすると詰まる。次のセクションがその話。


本番移行で詰まった3つのポイント

DatadogのAPMが動かなかった

最初のハマり。dd-traceはNode.jsのV8内部フック、特に--requireフラグとインスペクターAPIの特定の挙動に依存していて、Bunではその初期化シーケンスが違う。起動時のトレースが全く取れなくて30分くらい悩んだ。

調べたら、Datadog公式のBunサポートは2025年夏のGitHub issue段階では「experimental」のままで、1.3時点でも「完全対応」とは言えない状況だった。

解決策はOpenTelemetryベースへの切り替え。Datadog AgentはOTLPを受け付けるので、以下のように書き換えたら動いた。

// dd-traceの代替: OpenTelemetry SDKをBunで使う
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
})

sdk.start()

// BunではSIGTERMのハンドリングを明示的にやらないとshutdownが走らないことがある
process.on('SIGTERM', async () => {
  await sdk.shutdown()
  process.exit(0)
})

Datadogを使っているなら、OTLPへの移行を先に計画してからBun移行に入ること。これを後回しにすると確実に詰まる。

node:crypto のSubtle APIで金曜の午後にやらかした

これが一番焦った。金曜の午後にステージングから本番に上げたサービスで、JWTの検証が一部通らなくなった。具体的にはRSA-PSS署名の検証で、Node.jsとBunのWebCrypto実装の挙動が微妙に違ってた。

最初は「Bunのバグか?」と思ったんだけど——調べたら逆で、BunはW3C仕様に沿った実装をしていて、Node.jsの方が独自の挙動をしていた(Bunのissue trackerで同様の議論がいくつか見つかった)。

正直、「どちらが仕様的に正しいか」はどうでもよかった。動いていたものが動かなくなった、それだけが問題で。結局、JWTライブラリ側でjoseに切り替えて、直接SubtleCryptoを触るコードをラップすることで回避した。本番への影響は最小限で済んだけど、crypto周りは特に注意が必要。

bun:testはJestではない

これは完全に自分の思い込みが原因。BunのテストランナーはJestと似た構文なので「そのまま動くだろ」と思って移行し始めたら、jest.spyOnのリストア挙動や、一部のモジュールモック機能が微妙に違った。基本的なテストは動いたけど、既存テストの約20%に修正が必要だった。小さくないコスト。


Node.jsが依然として有利な場面

Bunを推す記事は起動速度とメモリ効率を強調しがちだけど、2026年3月時点でも、Node.jsの方が明確に有利な場面がある。

ネイティブモジュールsharp(libvipsのラッパー)、canvasnode-sqlite3など、node-gypで書かれたモジュールは動かないものがまだある。うちのケースでも、画像処理サービスがsharpに依存していて、そこはBunへの移行を断念した。Bunのネイティブモジュールサポートは改善を続けているけど、「全部動く」とは言えない。

安定性と情報量の話もある。Node.jsは10年以上の本番実績があって、LTSサポート、CVE対応の速さ、クラウドプロバイダーのドキュメント——全部Node.jsが一日の長がある。Bunでエラーが出たとき、まだ「Bun Discordで聞く」という体験が数回あった。Stack Overflowで即解決できるNode.jsとは情報の密度が違う。これは経験則だけど、無視できない。3年以上無人で走るバッチ処理や重いETLジョブをBunに突っ込む気にはまだなれないのも、結局ここに帰着する。実績年数がまだ足りない。


2026年3月時点での俺の結論

実験の結果、うちのチームは新規のワーカーサービス2本をBun + Honoで書き直して本番投入した。既存のメインAPIはNode.jsのまま。

バンドラーとしてのBunは、ランタイムより先に試す価値がある。既存のwebpackやesbuildをBunのバンドラーに置き換えたら、ビルド時間が体感で5〜6倍速くなった。ランタイム移行より摩擦が少ないので、まずそこから始めることをすすめる。

Bunが本領を発揮するのは、TypeScriptをそのまま動かしたい軽量な新規サービス、コールドスタートが問題になっているLambda/コンテナ環境、エコシステムの制約を受けない選択ができる新規プロジェクトだ。一方で、既存の重いミドルウェアスタックを抱えているサービス、APMやセキュリティツールがNode.js前提になっている環境、ネイティブモジュール依存が多いサービスは——正直まだNode.jsで行った方が楽。これは「Bunが劣っている」という話ではなく、成熟度とエコシステムの問題。

「結局どっちが勝ち?」という問いへの答えは——Node.jsは成熟した信頼性、Bunはスピードと開発体験、という差は本物だった。ただ、移行コストを甘く見ると痛い目を見る。特にAPM、crypto、テストランナー周りは必ず検証してから進めてほしい。

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top