Trench benchmark: evaluating elixir/beam and Bun against Node.js in production on Linux Mint
Back to blog

Trench benchmark: evaluating elixir/beam and Bun against Node.js in production on Linux Mint

6/7/2026 · 11 min · Development

Trench Benchmark: Evaluating Elixir/BEAM and Bun against Node.js in Production on Linux Mint#

The technology market experiences frequent cycles promoting new runtimes under the promise of low latency and unlimited concurrency. In high-availability corporate environments, maintaining a stable infrastructure requires rigorous validation. The operational stack is based on a consolidated setup: Node.js, pnpm, Prisma ORM, and PostgreSQL instances. However, the maturity of Elixir (Phoenix) and the execution efficiency brought by Bun motivated a comprehensive performance and technical compatibility audit.

This analysis documents a structured comparison between these runtimes, detailing their execution models, network architecture, security, observability, and performance metrics under Linux Mint operating systems.


1. The viability of transitioning from Node.js#

The decision to move away from a widely adopted ecosystem like Node.js is not just about raw processing speed. It involves maintenance costs, team training, and code rewrite effort. While the JavaScript ecosystem has the largest base of developers and libraries in the world, chronic issues related to high-scale memory consumption and event loop blocking prompt architects to evaluate alternatives like the Erlang Virtual Machine (BEAM) or Bun's optimized runtime.


2. Concurrency scope: the erlang virtual machine (BEAM)#

The Elixir ecosystem relies on the robustness of the BEAM (Erlang Virtual Machine), which manages execution through the Actor Model. Instead of heavy operating system threads, the BEAM manages Erlang processes that are extremely lightweight, consuming about 1 KB to 2 KB of memory each at creation time.

In practical terms, instead of handling concurrency by sharing memory (which requires complex locks and exposes the system to race conditions), Erlang processes communicate exclusively via asynchronous message passing. Fault tolerance is structured according to the let it crash philosophy:

This ensures that an error in an isolated request does not affect the global stability of the server runtime.


3. Phoenix liveview and the state-of-the-art flow#

Phoenix LiveView changes the traditional web development paradigm by unifying state control on the server and transmitting dynamic updates to the browser over persistent WebSocket connections.

Operational trade-offs: While this approach eliminates the complexity of building large bundles on the client, it transfers the processing and memory persistence load to the server. Each active WebSocket connection maintains a stateful process in memory on the BEAM, requiring higher RAM provisioning in scenarios with millions of concurrent connections.


4. The cost of architectural debt in refactoring#

Migrating from a Node.js-based system to Elixir imposes a shift in computing paradigms. Moving from an imperative or object-oriented model to pure functional programming requires rewriting the entire codebase.


5. Ecto vs. prisma and dependency management#

The data access and persistence layer also differs radically between the stacks:

In dependency management, while pnpm in the JavaScript ecosystem accelerates installations using a centralized content-addressable store and creating hard links/symlinks under the node_modules/ directory, Mix (Elixir's build tool) manages dependencies natively through the Hex repository, downloading and compiling isolated packages directly into the project's dependency folder deps/ without using symlinks.


6. Migration strategy: the strangler fig pattern#

Replacing an active legacy Node.js monolith all at once is financially unviable and introduces severe risks of business interruption. The recommended technical approach to mitigate this risk is applying the Strangler Fig pattern:

graph TD Client[Web Client / App] -->|Request| Gateway[API Gateway / Nginx] Gateway -->|Legacy Routes| NodeService[Node.js Monolith] Gateway -->|New Routes / WebSocket| ElixirService[Phoenix / BEAM Service] NodeService --> DB[(PostgreSQL)] ElixirService --> DB

By isolating routes and services behind an API Gateway (such as Nginx or HAProxy), legacy Node.js APIs continue to process default requests while new critical routes or high-concurrency connections based on WebSockets are incrementally built in Elixir.


7. Node.js execution model: event loop and concurrency#

The Node.js concurrency model is based on the Event Loop executed on a single main thread (Single-Threaded for JavaScript code execution). However, the statement that Node.js is entirely single-threaded is conceptually incomplete.

If the main JavaScript thread executes a blocking CPU-heavy task, the Event Loop will freeze, resulting in latency and timeouts for active HTTP connections.


8. Bun's acceleration and the javascriptcore engine#

Unlike Node.js and Deno, which use Google's V8 engine, Bun was developed using WebKit's JavaScriptCore (JSC) engine, maintained by Apple.

Bun's compatibility with legacy ecosystems has been enhanced, resolving past bugs with the Prisma Query Engine on local file systems.


9. The role of io_uring in Linux performance#

One of Bun's key performance advantages on Linux platforms is native support for io_uring, a modern asynchronous Linux Kernel subsystem introduced in version 5.1.

Note that modern versions of Node.js's libuv also optionally support enabling io_uring on compatible Linux environments.


10. The relevance of the operating system platform#

Running benchmarks on the Linux Mint operating system (a stable distribution based on the Ubuntu/Debian ecosystem) provides a highly consistent environment for evaluating runtimes. Metrics collected on Mint are directly equivalent to production servers running Ubuntu LTS. To guarantee technical accuracy, kernel configurations (such as the maximum number of open socket file descriptors) must be optimized by editing directives in the system configuration file /etc/security/limits.conf:

# Example of system limits hardening in /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536

11. Production benchmark methodology#

The methodology used in the Trench Benchmark followed strict software engineering parameters to ensure results reproducibility:


12. Results and quantitative metrics#

The raw metrics consolidated during the stress testing under concurrent load of 1,000 virtual clients revealed the following performance patterns:

Comparative performance table#

Metric CollectedNode.js (LTS) + PrismaBun.js + PrismaElixir / Phoenix + Ecto
Maximum Throughput (Req/s)12,450 req/s24,120 req/s18,980 req/s
Average Latency (p50)12.4 ms4.2 ms5.8 ms
Tail Latency (p99)88.2 ms18.9 ms8.9 ms
Average RAM Consumption145 MB72 MB110 MB
Cold Start Time120 ms15 ms210 ms

Cold start time (measured as the time elapsed between binary execution and the ability to respond to the first HTTP request) was evaluated to assess behavior in serverless topologies and elastic container scaling:

Cold Start Runtimes:
Node.js:  ████████████ 120ms
Bun:      █ 15ms
Elixir:   █████████████████████ 210ms

Analysis: Bun demonstrated the highest pure throughput for simple requests and the lowest startup time, benefiting from the JavaScriptCore engine. Conversely, Elixir presented the most stable and predictable p99 latency under extreme stress, proving that the BEAM's preemptive garbage collector mitigates cyclic latency spikes (Stop-The-World garbage collection) common in Node.js's V8.


13. Runtime security in Linux environments#

When operating Node.js, Bun, or BEAM environments in production, hardening the operating system is mandatory to mitigate process-level attack vectors.

Isolation and privilege guidelines#

  1. Least Privilege: Never run mail services or application runtimes as the root user. Create a dedicated user with reduced shell privileges:
   # Create a user without administrative privileges
   useradd -M -s /usr/sbin/nologin app_runtime
  1. Directory Hardening: Ensure that dependencies downloaded in the project root are not writable by the active application process.
  2. Process Isolation: Use Linux Kernel namespaces (via Docker or Systemd Sandboxing) combined with restrictive Seccomp (Secure Computing Mode) profiles to prevent Bun (compiled in Zig) from executing unauthorized syscalls that could compromise host security.

14. Observability and SRE monitoring#

To gain visibility into the internal behavior of running applications, SRE teams must implement structured monitoring based on OpenTelemetry (OTel).


15. Version management with fnm and asdf#

In development environments on Linux Mint, installing global packages via sudo npm install -g pollutes the /usr/lib/ system directory and introduces security risks. A conservative approach suggests managing version tools in the user directory (User-Level).

To install and configure fnm (Fast Node Manager), a fast manager written in Rust, add the environment variables to your terminal profile files:

# Download and install fnm in the local user directory
curl -fsSL https://fnm.vercel.app/install | bash

After installation, configure the PATH by adding these lines to the end of your ~/.bashrc or ~/.zshrc file:

# Initialize fnm in the terminal
export PATH="$HOME/.local/share/fnm:$PATH"
eval "`fnm env`"

As an alternative for managing multiple stacks (managing Node, Elixir, Erlang, and Python in one tool), adopt asdf, or use dedicated managers like nvm or volta:

# Local initialization of plugins in asdf
source $HOME/.asdf/asdf.sh

16. Runtime decision matrix#

To select the best architecture based on project requirements, refer to the parameters defined in the comparative table below:

Project RequirementNode.js (LTS)Bun.jsElixir / Phoenix
Legacy CodebaseRecommendedCompatibility SupportTotal Rewrite Required
WebSocket ConcurrencyMediumHigh PerformanceIdeal (BEAM Scale)
Low Startup TimeMediumIdeal (15ms)High
Functional ProgrammingNon-nativeNon-nativeNative Paradigm
Developer EcosystemMaximumHigh (JavaScript)Restricted / Specialized

17. SRE checklist for stack migration assessment#

Before initiating a transition from Node.js to new stacks, validate the following phases:


18. Operational risk assessment matrix#

Risk EventSeverityTechnical ImpactRecommended Mitigation
Event Loop BlockingHighHeavy synchronous CPU tasks freeze Node.js main thread, blocking connections.Delegate heavy CPU tasks to Worker Threads or dedicated Elixir microservices.
Bun API IncompatibilitiesMediumBun uses JavaScriptCore; API differences from V8 may cause unexpected runtime errors.Execute comprehensive integration test suites before deploying to production.
WebSocket Memory LeaksHighThousands of stateful LiveView connections keep processes active in BEAM memory, causing RAM exhaustion.Configure aggressive idle timeouts for WebSockets and monitor VM RAM metrics.
Insecure Install PermissionsMediumRunning sudo npm install or global root installs exposes the system to malicious code in dependencies.Adopt local managers (FNM or Volta) installed in the user directory without root privileges.
Additional Latency in Strangler FigLowProxying requests through the API Gateway between Node and Elixir introduces minor latency.Optimize local network connections and configure persistent keep-alive in the Nginx gateway.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments