Daniel Murillo
Index

Architecture · October 28, 2026 · Daniel Murillo

Node.js vs Bun vs Deno in 2026: an architecture decision

The best runtime is the one that meets your requirements with the right balance of performance, compatibility, security, and operational cost.

The best runtime is the one that meets your application’s requirements with the right balance of performance, compatibility, security, complexity, and operational cost.

Introduction: three runtimes, different priorities

Node.js, Bun, and Deno run JavaScript, but they do not pursue exactly the same proposal.

  • Node.js bets on a broad ecosystem, compatibility, and a mature operational platform.
  • Bun combines a JavaScriptCore-based runtime with integrated tools and aims for high performance across multiple scenarios.
  • Deno emphasizes explicit permissions, web standards, and integrated tools, while expanding compatibility with Node and npm.

The comparison becomes confusing when you simply ask which is “the best.” One runtime can be excellent for scripts and CLIs, another can reduce risk for an enterprise platform, and another can fit better when permissions and web APIs are priorities.

The decision should not be a popularity contest or a table sorted by requests per second. It should start from the load, the dependencies, and the operational capabilities the project needs.

1. Executive summary

DimensionNode.jsBunDeno
EngineV8JavaScriptCoreV8
PriorityCompatibility and ecosystemPerformance and integrated toolsSecurity, web standards, and tooling
TypeScriptBuilt-in support for common syntax in recent versions; separate type-checkBuilt-in execution/transpilation; separate type-checkBuilt-in execution and deno check
Packagesnpm, pnpm, Yarnbun install and npm packagesnpm, JSR, and lockfiles from several managers
Node APIsPrimary referenceBroad compatibility, not totalGrowing compatibility, not total
I/O permissionsUsually per process user and OSNot the central differentiatorExplicit permissions for files, network, environment, and processes
Representative frameworksExpress, Fastify, NestJS, AdonisJS, HonoElysia, Hono; also many compatible Node appsHono, Fresh, Oak; many npm dependencies with validation
Typical strengthMature dependencies and known operationsStartup, tooling, and certain performance workloadsPermission model and integrated experience
Main cautionFragmented tooling and need to govern dependenciesCompatibility and observability differencesConcrete dependency compatibility and operational permissions

The table is an initial guide, not an absolute ranking. Capabilities evolve with each version.

2. Do not mix the layers

To compare rigorously, separate four layers:

  1. JavaScript engine: V8 or JavaScriptCore.
  2. Runtime: networking, files, processes, modules, timers, and system tools.
  3. Framework: routing, middleware, validation, dependency injection, and conventions.
  4. Application: business logic, database queries, authentication, cache, logging, and third-party calls.

A Bun.serve() benchmark is not a fair comparison against a NestJS application. Nor does an Express-with-TLS test let you conclude which runtime will be better in a service that spends most of its time waiting on PostgreSQL.

The comparison must state which layer it is measuring.

3. Published benchmarks: useful evidence, not a final verdict

Project benchmarks help identify hypotheses. They should not be mixed as if they all came from the same machine and methodology.

Express over HTTPS

Bun publishes an Express 5.2.1 test with TLS 1.3, bombardier, Linux x64, and AMD EPYC. The data are medians of three runs:

RuntimeVersionRequests/sPeak RAM
Bun1.448 243105 MB
Node.js26.7.025 181142 MB
Deno2.9.519 243176 MB

In this test, Bun achieves about 1.9× Node’s throughput and 2.5× Deno’s. That is relevant for investigating Express behavior on those runtimes, but it does not prove that any business API will improve in the same proportion.

Source: Bun — benchmarks

PostgreSQL: be careful comparing different drivers

The same page compares a PostgreSQL load with 100 rows per query, 100 in-flight queries, and 100 000 total queries. Bun uses its built-in SQL API, while Node and Deno use postgres.js 3.4.9.

Runtime / driverQueries/sPeak RAM
Bun 1.4 (Bun.sql)20 24380 MB
Deno 2.9.5 (postgres.js)10 406181 MB
Node.js 26.7.0 (postgres.js)10 000168 MB

It is not a pure runtime comparison. The database client also changes, so the result represents the implementation and configuration set. For a controlled comparison, use the same driver if available and verify that each version does exactly the same work.

Source: Bun — benchmarks

Deno 2.9 versus Deno 2.8

Deno reported these improvements on its Deno.serve server, with concurrency 100, on a dedicated Linux x86_64 machine:

MetricDeno 2.8Deno 2.9
Cold start34.2 ms17.3 ms
HTTP realworld56.8k req/s72.4k req/s
HTTP text77.0k req/s85.6k req/s
1 MiB response1 617 req/s1 907 req/s
RSS, realworld142 MB64 MB

It is an improvement between Deno versions, not a direct comparison with Bun or Node. The benchmark’s “realworld” load receives JSON with a bearer token and returns it as JSON, but it remains a simplified application.

Source: Deno 2.9 — performance

Streams: optimizing a concrete API matters

Bun 1.4 published a Response.clone() test with a 64 MB body. In the configuration described by the project, Bun reports a 220 MB memory peak and 96 ms, Node.js 26 reports 382 MB and 230 ms, and Deno 2.9 reports 297 MB and 134 ms.

Bun’s article explains that an optimization avoids copying the full body on the second branch. It is an important advantage for that operation, not evidence that every stream manipulation is always faster.

Source: Bun 1.4 — performance

What conclusion can be drawn?

  • Bun shows important advantages in several scenarios published by its team.
  • Deno 2.9 improved considerably versus Deno 2.8 in the tests it published.
  • Node.js remains a competitive option, but its broader advantage usually lies in ecosystem and compatibility, not in winning every microbenchmark.
  • You cannot build a universal ranking by mixing tables with different scenarios.
  • A real application must be measured with its dependencies, configuration, limits, and SLOs.

4. Which runtime to choose by project?

Enterprise platform with many dependencies

First option to evaluate: Node.js.

It is especially reasonable when the application depends on cloud SDKs, native modules, drivers, APM agents, and libraries whose primary support is Node. The value is in reducing uncertainty, easing diagnosis, and leveraging team experience.

Bun and Deno remain possible options, but you would need to validate every critical integration and compare the cost of maintaining the differences.

New TypeScript API

I would evaluate all three.

  • Node.js with Fastify or NestJS offers a mature base.
  • Bun with Bun.serve() or Elysia can be attractive if performance or integrated tooling matter.
  • Deno with Hono can be interesting if web APIs, permissions, and integrated tools are valued.

The final choice depends on dependencies, the team’s architectural style, and the results of a representative test.

CLIs, scripts, and automation

Bun and Deno deserve special attention.

Their integrated tools can simplify development and startup. Deno provides a useful permission model for scripts that should not have unrestricted access to the whole system. Bun can be attractive for its speed and its integration of package manager, test runner, and bundler.

Node remains a good choice when the script depends on Node-specific packages or APIs.

Service with permission-based security requirements

I would evaluate Deno.

Its explicit permission model lets you limit access to files, network, environment, and processes. The advantage is especially useful when those limits are part of the design. It does not replace OS isolation, dependency review, or deployment controls.

WebSockets, streaming, or high throughput

Do not choose by the runtime name.

Bun publishes interesting WebSockets and stream results; Deno has improved its HTTP server; Node has a mature ecosystem. But differences depend on message pattern, payload size, fan-out, TLS, backpressure, serialization, and delivery confirmation.

Run a load test with the real topology: number of connections, messages per second, message size, latency percentiles, memory, and reconnects.

5. Frameworks: choose them separately from the runtime

FrameworkUsual environmentWhy evaluate it
ExpressNode.jsExtensive ecosystem and simplicity
FastifyNode.jsValidation, serialization, plugins, and low overhead
NestJSNode.jsModular structure, dependency injection, and conventions
ElysiaBunDesign tightly integrated with Bun and TypeScript
HonoNode.js, Bun, Deno, and othersWeb APIs and portability
FreshDenoWeb applications with server rendering and islands
OakDenoMiddleware framework with a Koa-inspired style

The columns do not mean a framework cannot run outside its usual environment. Real compatibility depends on the APIs, plugins, and dependencies the project uses.

If portability matters, using web standards and isolating runtime APIs can reduce coupling. If you need a framework with a very specific ecosystem, prioritize compatibility and operational experience over a theoretical abstraction.

6. A fair benchmarking methodology

A useful benchmark should answer a concrete question. Before running tests, define the hypothesis: do you want to reduce cost per request, improve p99, lower memory per replica, speed up cold starts, or reduce CI time?

Controlled conditions

  1. Pin exact versions of runtime, framework, and dependencies.
  2. Use the same hardware, operating system, and container limits.
  3. Run the same business work and, when possible, the same framework.
  4. Use the same dataset, connection pool, cache, and TLS configuration.
  5. Verify that the load generator is not the bottleneck.
  6. Repeat runs and record variability.
  7. Separate cold start from sustained performance.
  8. Document code and configuration so another team can reproduce the result.

Metrics

  • Throughput: completed requests per second.
  • Latency p50, p95, and p99.
  • CPU per request.
  • RSS memory and heap evolution.
  • Startup time.
  • Errors, timeouts, and rejected connections.
  • Stability under sustained load and during spikes.
  • Infrastructure cost to meet the SLO.

Do not report only requests per second. A server can produce more throughput at the cost of a longer latency queue or memory use that forces more replicas.

Three scenarios

A. Minimal endpoint: approximates the server’s ceiling.

B. Representative API: routing, validation, authentication, serialization, and data access.

C. Production simulation: TLS, tracing, logs, container limits, sustained load, and dependency failures.

Test B or C should weigh more in the decision than test A.

7. Costs that do not appear in a performance table

Compatibility cost

A dependency that does not work can cost more than a throughput improvement. Count critical integrations, incompatible APIs, patches, and maintenance hours.

Operational cost

Evaluate support on deployment platforms, base images, health checks, graceful shutdown, workers, observability, profiling, and incident response.

Learning cost

A team expert in Node can deliver faster with Node even if another runtime is superior in a benchmark. A new team may prefer Bun’s or Deno’s integrated tooling. Learning cost is part of the decision.

Infrastructure cost

If a runtime reduces memory or CPU on a real load, it could lower the number of replicas. But that improvement must hold with dependencies, logs, and production limits. Calculate cost per traffic volume or completed task, not only peak speed.

Migration cost

Migrating a running system implies tests, rollout, observability, rollback, and training. A marginal improvement on one endpoint usually does not justify that cost.

8. Weighted decision matrix

Score each criterion for your project from 1 to 5 and assign a weight. Do not use a universal score for every team.

CriterionSuggested initial weightNode.jsBunDeno
Compatibility with critical dependencies5Usually the referenceValidate APIs and native modulesValidate APIs and native modules
Performance on the real load5MeasureMeasureMeasure
Security via runtime permissions4Requires additional controlsNot its main differentiatorCentral differentiator
Integrated tooling3Modular ecosystemIntegratedIntegrated
Observability and operational support5Very broad ecosystemTest APM and profilingTest APM and profiling
Team experience4Depends on the teamDepends on the teamDepends on the team
Startup time2–5 depending on the caseMeasureMeasureMeasure
Cost per served load5MeasureMeasureMeasure
Portability across runtimes2–5 depending on the caseNode APIsBetter if web standards are usedBetter if web standards are used

The weights are a starting point, not an immutable recommendation. In a CLI, startup may have weight 5; in a business API dominated by SQL, startup speed may have little impact.

9. Adoption strategy with controlled risk

It is not mandatory to standardize an entire organization on a single runtime from day one. A reasonable strategy is:

  1. Keep the current runtime as the baseline.
  2. Select a small service with bounded dependencies.
  3. Automate functional and compatibility tests.
  4. Compare performance under controlled conditions.
  5. Test observability, containers, health checks, and shutdown.
  6. Deploy gradually with metrics and rollback.
  7. Expand usage only if the benefits are sustainable.

Avoid ending up with three runtimes by fashion. Diversity can be useful, but it adds versions, pipelines, documentation, and skills to maintain. Introduce a new platform when it solves a concrete problem.

10. Final recommendation

Choose Node.js when compatibility, ecosystem, and known operations are priorities, especially in large systems with many dependencies and integrations.

Choose Bun when integrated tooling, startup, or performance measured on your load provide a concrete benefit and you can validate critical dependencies.

Choose Deno when the permission model, web APIs, and integrated development flow fit the security and architecture requirements.

Do not turn these tendencies into absolute rules. Node.js can be more appropriate for a new API; Bun can be a great choice for an enterprise system; Deno can be a valid option even if the team uses some npm libraries. What matters is the fit between requirements and capabilities.

The best decision is not the runtime that wins a benchmark published by its own team. It is the one that meets latency, cost, security, and maintainability goals with a level of risk the team can sustain.

Measure your application. Review the dependencies. Decide according to the problem.

In this series

  1. Node.js: why it is still a solid bet
  2. Bun: is it really faster than Node.js?
  3. Deno: security, TypeScript, and tooling
  4. Final comparison (this post)

Related reading

References

Benchmarks are attributed to the projects that publish them. Results do not form an independent universal ranking: they vary by version, load, hardware, framework, driver, and configuration. Before a production decision, reproduce the relevant tests and add a representative test of your application.