LabHub

Blog

Modern Erlang and BEAM in 2026 — OTP 27, OTP 28 Preview, Cowboy 3, Bandit, Khepri, Gleam, Nerves, AtomVM Deep Dive

한국어English日本語

Prologue — Why a 30-Year-Old VM Is Still Compelling in 2026

  1. A team at Ericsson building telecom switches creates a new language. Its name: Erlang. Its single purpose: systems that do not die. Telephone exchanges with nine 9s of availability — 99.9999999%.

Forty years later, in 2026, Erlang is no longer just a telecom language. WhatsApp runs two billion users' messages on BEAM. Discord routes voice and text for hundreds of millions of people through Elixir. Klarna processes payment transactions on Erlang. RabbitMQ sits on the backbone of essentially every microservice architecture, and Akamai's DNS runs on Erlang.

BEAM has not stood still. OTP 27 shipped in May 2024, and OTP 28 is moving from preview to GA. Cowboy 3 has reworked HTTP/2 and WebSocket. Bandit offers a pure-Elixir alternative to Cowboy. The RabbitMQ team put Khepri on top of Raft as a Mnesia successor. gleam brought static types to BEAM. Nerves has captured embedded Linux, and AtomVM has pushed even into ESP32 microcontrollers.

This article is a full map of the BEAM ecosystem in 2026. New features in OTP 27 and 28, the HTTP stack, distributed data, embedded, new languages, and who actually uses this in production.

"Erlang lets us get more done with fewer people." — Jan Koum, WhatsApp co-founder, back when 50 engineers were running 900 million users.


1. The 2026 Erlang/BEAM Map — Why It Still Compels

First, what BEAM is and why it is fundamentally different from other virtual machines.

Four Core Assets of BEAM

  1. Lightweight processes (green processes) — Not OS threads. Virtual processes inside BEAM. About 300 bytes each. You can spawn millions on a single node.
  2. Preemptive scheduler — Different from Go's cooperative scheduler. BEAM uses reduction counts to force context switches. One process spinning in an infinite loop will not starve the others.
  3. Share-nothing message passing — No shared memory between processes. All communication is via messages. No locks. Whole categories of deadlocks simply disappear.
  4. OTP (Open Telecom Platform) — Behaviors like supervisor, gen_server, gen_statem. A 30-year-validated standard library for distribution and fault tolerance.

Where BEAM Shines vs Where It Doesn't

Shines                                    Doesn't shine
────────────────────────                  ────────────────────
Massive concurrent connections (millions) CPU-bound number crunching
Real-time messaging / chat                Single-threaded throughput
Distributed systems / clustering          Machine learning training
Long-lived connections (LiveView)         Desktop GUI apps
Fault-tolerance-critical systems          Systems programming
Protocol gateways (MQTT, AMQP)            Game engines

The principle: places where concurrency, not CPU, is the bottleneck. And places where death is not allowed.

One-Page View of the 2026 BEAM Ecosystem

              ┌────────────────────────────────────────┐
              │             BEAM VM                    │
              │      (JIT, ETS, supervisor, OTP)       │
              └───────┬────────────────┬───────────────┘
                      │                │
        ┌─────────────┼───┐    ┌───────┼──────────┐
        │             │   │    │       │          │
     Erlang        Elixir gleam    Lustre     Phoenix
       │              │     │       (web)   (LiveView)
       │              │     │
       ▼              ▼     ▼
  ┌────────┐     ┌─────────┐  ┌─────────┐
  │Cowboy 3│     │ Bandit  │  │ AtomVM  │
  │  HTTP  │     │(for Plug│  │ (ESP32) │
  └────────┘     └─────────┘  └─────────┘

  Storage:      Mnesia / Khepri (Raft) / ETS / DETS
  Distribution: distributed Erlang / partisan
  Embedded:     Nerves (Linux) / AtomVM (MCU)
  Foundation:   Erlang Ecosystem Foundation (EEF)

The rest of this article walks each part of that map in order.


2. Erlang/OTP 27 (May 2024) — Rediscovering String Interpolation and persistent_term

Erlang/OTP 27 shipped in May 2024. As of May 2026 most production systems are either on 27 or migrating to it. The three most visible changes.

2-1. Triple-Quoted Strings and String Interpolation

Erlang lived for 30 years without string interpolation. It finally landed in 2026 — and in two forms.

%% Triple-quoted string (OTP 27)
Greeting = """
   Hello, World!
   Multi-line, no escaping needed.
   """,

%% Sigil form (~b for binary)
Name = <<"Alice">>,
Greeting2 = ~b"Hello, #{Name}!".
%% => <<"Hello, Alice!">>

The ~b sigil produces a binary string with interpolation, and ~B produces a non-interpolated binary. The design is similar to Elixir's ~s and ~S sigils.

Reality: Erlang users have written io_lib:format("Hello, ~p!", [Name]) for thirty years. Now it can be ~b"Hello, #{Name}!". Code reads much better.

2-2. set_node_lookup_node / Node Discovery Improvements

Node discovery in distributed Erlang traditionally relied on EPMD (Erlang Port Mapper Daemon). OTP 27 formalized a callback API that lets you bypass EPMD and register node names directly.

2-3. persistent_term Improvements — Faster Reads

persistent_term is the cousin of ETS used for almost-never-changing global constants. Writes are very expensive (they trigger a system-wide GC), but reads are essentially zero cost.

OTP 27 cleaned up the internals to reduce memory footprint and write-time GC cost. Even better for almost-permanent data like config, keys, or routing tables.

%% Write once, read billions of times
persistent_term:put({config, max_connections}, 100000),

%% Almost free on the hot path
Max = persistent_term:get({config, max_connections}).

2-4. JIT Improvements and Compiler Diagnostics

OTP 27 Migration Checklist

□ Update Rebar3 / Mix to OTP 27 compatible versions
□ Re-run Dialyzer (subtle type-system changes)
□ If using EPMD replacement callbacks, check net_kernel config
□ Triple-quoted strings need IDE support (Erlang LS, ElixirLS)
□ Verify any third-party deps that pin to older OTP (most are fine)

3. OTP 28 Preview — What's Coming Next

As of May 2026, OTP 28 is in release candidate. GA is targeted for summer 2026. The headline changes.

3-1. Additional JIT Optimizations

3-2. Standard Library Extensions for set and map

%% Expected new helpers in OTP 28
maps:groups_from_list(fun(X) -> X rem 2 end, [1,2,3,4,5]).
%% => #{0 => [4,2], 1 => [5,3,1]}

%% Analogous helpers on the set module

3-3. Modernized SSL Stack — TLS 1.3 Completion

3-4. Tighter logger and telemetry Integration

3-5. Distributed Monitoring Improvements

Summary: OTP 28 is not a revolution. It is polish. It is a great example of how a 30-year-old system keeps getting better, year after year.


4. Cowboy 3 — BEAM's Default HTTP Server

Cowboy is the small, fast Erlang HTTP server created by Loïc Hoguin. It underpins Plug, Phoenix, the RabbitMQ Management UI, and basically every Erlang or Elixir web stack as a default.

Cowboy 3 landed in late 2024. The headline changes.

4-1. Full HTTP/2 Compliance

4-2. WebSocket Stabilization

4-3. HTTP/3 — Experimental

4-4. Simple Cowboy Handler Example

%% After adding cowboy to rebar.config

-module(hello_handler).
-export([init/2]).

init(Req0, State) ->
    Req = cowboy_req:reply(200,
        #{<<"content-type">> => <<"text/plain">>},
        <<"Hello from Cowboy 3!">>,
        Req0),
    {ok, Req, State}.

%% Register on the router
start() ->
    Dispatch = cowboy_router:compile([
        {'_', [{"/", hello_handler, []}]}
    ]),
    {ok, _} = cowboy:start_clear(my_http_listener,
        [{port, 8080}],
        #{env => #{dispatch => Dispatch}}).

Notable Systems Using Cowboy


5. Bandit — A Pure-Elixir HTTP Server

Cowboy is written in Erlang. Plug/Phoenix is written in Elixir. The bridge has historically been an adapter (Plug.Cowboy), but some users wanted "what if the entire Elixir stack were Elixir?"

Bandit (started in 2022, led by Mat Trudel) is that answer.

5-1. What Bandit Aims For

Cowboy:   Erlang  -> Elixir adapter (Plug.Cowboy) -> Plug/Phoenix
Bandit:   Elixir  ->                                -> Plug/Phoenix

5-2. Integration with Phoenix

Since Phoenix 1.7, Bandit can be used as a drop-in replacement for Plug.Cowboy.

# config/config.exs
config :my_app, MyAppWeb.Endpoint,
  adapter: Bandit.PhoenixAdapter,
  http: [port: 4000]

One line. Phoenix LiveView, channels, controllers all keep working.

5-3. Bandit vs Cowboy — 2026 Comparison

                      Cowboy 3            Bandit
Host language         Erlang              Elixir
HTTP/1.1              Production          Production
HTTP/2                RFC 9113            RFC 9113
HTTP/3                Experimental        Not yet
WebSocket             Yes                 Yes
Plug integration      Adapter             Native
Ops track record      10+ years           3 to 4 years
Recommended for       Conservative shops  New Phoenix projects

5-4. Which One to Choose


6. Khepri — The RabbitMQ Team's Mnesia Successor

Mnesia is the distributed DBMS that Erlang has shipped with since the 1990s. Hybrid memory and disk, transactions, ETS-compatible interface. But its netsplit behavior is a well-known weak spot — when a split happens, both sides keep running, the data diverges, and manual intervention is needed to rejoin.

The RabbitMQ team (under Pivotal, then VMware, now Broadcom) built Khepri to attack that problem head-on.

6-1. The Core Idea of Khepri

"Replace Mnesia, but put Raft consensus under it."

6-2. The Khepri Data Model

%% Path-based tree structure
ok = khepri:put([app, config, max_users], 1000),
{ok, 1000} = khepri:get([app, config, max_users]),

%% Wildcard queries
{ok, Map} = khepri:get_many([app, config, ?KHEPRI_WILDCARD_STAR]).

The design is very close to Zookeeper's znodes and etcd's key tree.

6-3. Adoption in RabbitMQ

6-4. Using Khepri in Your Own System

%% Add khepri to rebar.config
{deps, [
    {khepri, "0.14.0"}
]}.

%% Start it
{ok, _} = khepri:start(),

%% Transaction
khepri:transaction(fun() ->
    khepri_tx:put([users, alice], #{role => admin}),
    khepri_tx:put([users, bob], #{role => user})
end).

Bottom line: Khepri is quietly replacing Mnesia. For new distributed BEAM systems that need a metadata store, Khepri is increasingly the sensible default.


7. Mnesia and partisan — Distributed Data and Distributed Communication

7-1. Mnesia — Still Alive

The arrival of Khepri does not erase Mnesia. In 2026:

Rules of thumb:

7-2. partisan — A Rethink of Distributed Erlang Itself

The default distributed Erlang (net_kernel) assumes a full-mesh topology. Every node is directly connected to every other node. Past about 100 nodes you start to see message storms and head-of-line blocking.

partisan (Christopher Meiklejohn and others) redesigned this.

distributed Erlang:  N nodes -> N*(N-1)/2 connections (full mesh)
partisan:            Multiple topology options
                     - full mesh
                     - hyparview (peer-to-peer overlay)
                     - client-server
                     - PG2-based static groups

The key ideas:

  1. Multi-channel — multiple TCP connections between a pair of nodes. Small messages no longer get blocked behind large ones.
  2. Monolithic or P2P — pick a topology.
  3. Backwards-compatible — keep the gen_server interface; swap the transport underneath.

As of 2026, partisan is:


8. Nerves — Embedded Erlang/Elixir

Nerves, led by Frank Hunleth, is an embedded framework with a single big idea:

"Run BEAM on Linux. The entire userland is Elixir/Erlang."

8-1. What Nerves Aims For

8-2. A Typical Nerves Project

# Create a new Nerves project targeting Raspberry Pi 4
mix nerves.new my_iot_device --target rpi4

cd my_iot_device

# Build the firmware
MIX_TARGET=rpi4 mix deps.get
MIX_TARGET=rpi4 mix firmware

# Burn to SD card
MIX_TARGET=rpi4 mix firmware.burn

The firmware image bundles the bootloader, the Linux kernel, BEAM, and your Elixir application. There is almost nothing else in userland.

8-3. Real-World Use Cases

8-4. NervesHub — OTA Fleet Management

NervesHub, also built by the Nerves team, is a centralized firmware update system. You can do staged rollouts across a fleet of devices.

Point: Nerves is the empirical demonstration that "embedded works fine when it runs inside a BEAM supervisor."


9. gleam — A Statically-Typed BEAM Language

gleam, created by Louis Pilfold and started in 2016, is a statically-typed functional language that targets BEAM. It hit 1.0 and took seed funding in 2024, and started attracting real attention.

9-1. What gleam Offers

import gleam/io
import gleam/list

pub fn main() {
  let numbers = [1, 2, 3, 4, 5]
  let doubled = list.map(numbers, fn(n) { n * 2 })
  io.debug(doubled)
}

9-2. Why Bring Static Types to BEAM?

9-3. A Simple OTP-Style Actor (gleam_otp)

import gleam/erlang/process
import gleam/otp/actor

pub fn main() {
  let assert Ok(subject) = actor.start(0, handle_message)
  process.send(subject, Increment)
  process.send(subject, Increment)
  // incremented twice
}

pub type Message {
  Increment
  GetCount(reply_to: process.Subject(Int))
}

fn handle_message(message: Message, count: Int) -> actor.Next(Message, Int) {
  case message {
    Increment -> actor.continue(count + 1)
    GetCount(reply_to) -> {
      process.send(reply_to, count)
      actor.continue(count)
    }
  }
}

OTP, but typed. The shape of the messages a process can receive is checked at compile time.

9-4. What Has Happened Since gleam 1.0

9-5. When to Pick gleam


10. Lustre — gleam's Web Framework

Lustre, by Hayleigh Thompson, is gleam's frontend framework. It is heavily inspired by Elm and uses The Elm Architecture (TEA) pattern.

10-1. The Lustre Model

        ┌─────────┐     Msg     ┌─────────┐
        │  View   │ ───────────>│ Update  │
        │ (HTML)  │             │ (model  │
        └─────────┘ <────────│  │ changes)│
            ▲       Model       └─────────┘
            └──── user interaction ────┐
                                 (browser)

10-2. A Simple Counter (Lustre)

import lustre
import lustre/element/html.{button, div, text}
import lustre/event

pub fn main() {
  let app = lustre.simple(init, update, view)
  let assert Ok(_) = lustre.start(app, "#app", Nil)
}

fn init(_) {
  0
}

pub type Msg {
  Incr
  Decr
}

fn update(model: Int, msg: Msg) -> Int {
  case msg {
    Incr -> model + 1
    Decr -> model - 1
  }
}

fn view(model: Int) {
  div([], [
    button([event.on_click(Decr)], [text("-")]),
    text(int.to_string(model)),
    button([event.on_click(Incr)], [text("+")]),
  ])
}

10-3. Lustre's Two Modes

  1. Client-side — compile gleam to JavaScript and run in the browser.
  2. Server components — Phoenix LiveView style; server holds the state, the browser is a thin client.

The server-components mode is similar to LiveView but adds gleam's type safety as a differentiator.

10-4. Where Lustre Sits in 2026


11. AtomVM — Erlang on ESP32

AtomVM, by Davide Bettio and others, is a VM that runs BEAM bytecode on microcontrollers.

11-1. What AtomVM Targets

These boards are too small to run Linux (typically hundreds of kilobytes to a few megabytes of RAM). Nerves cannot go here, but AtomVM can.

11-2. How It Works

11-3. "Hello, Erlang" on ESP32

-module(blink).
-export([start/0]).

start() ->
    Pin = 2,
    gpio:set_pin_mode(Pin, output),
    loop(Pin, high).

loop(Pin, State) ->
    gpio:digital_write(Pin, State),
    timer:sleep(500),
    NextState = case State of
        high -> low;
        low -> high
    end,
    loop(Pin, NextState).
# Compile and flash to ESP32 (rough outline)
erlc blink.erl
atomvm_packbeam blink.avm blink.beam
esptool.py write_flash 0x250000 blink.avm

You start with the LED-blink classic and grow into BLE-based IoT devices, small message gateways, and so on.

11-4. AtomVM vs Nerves

            AtomVM                       Nerves
Target       MCU (ESP32, STM32)          SBC (Pi, BeagleBone)
RAM          Hundreds of KB              Multiple GB possible
OS           None (bare metal / FreeRTOS) Linux
BEAM         AtomVM (subset)             Full OTP
Use case     Sensor nodes, BLE devices   Edge gateways

The smallest node uses AtomVM, the hub that aggregates them uses Nerves, and the cloud backend uses full OTP. The whole hierarchy on the same family of language.

11-5. Where AtomVM Stands Today


12. RabbitMQ — The Poster Child of Erlang

RabbitMQ has been covered in its own deep dive elsewhere, so we will be brief. The key point is that this single project did more than any other to bring Erlang into mainstream infrastructure.

12-1. Why RabbitMQ Picked Erlang

12-2. RabbitMQ in 2026

12-3. The Effect on the Erlang Ecosystem


13. Real Usage — WhatsApp, Klarna, Discord, Riot, Cisco, Akamai

Short case studies of companies that take BEAM seriously.

13-1. WhatsApp — The Erlang Scale Legend

13-2. Klarna — Erlang at Fintech Scale

13-3. Discord — Erlang/Elixir Hybrid

13-4. Riot Games — Erlang for Chat

13-5. Cisco — XMPP / Telecom Infra

13-6. Akamai — DNS in Parts

Common Threads

Companies whose essential bottleneck is concurrency, not CPU.


14. Korea and Japan — BEAM in East Asia

BEAM is well-known in the West, but how does adoption look in East Asia?

14-1. Korea — Kakao, Telcos, Some Startups

Reality: BEAM is not mainstream in Korea, but in companies whose essence is messaging or real-time, it is quietly used.

14-2. Japan — Pixiv, NTT, Telecom

14-3. Why Adoption Looks Slower in East Asia


15. Who Should Pick BEAM — Decision Guide

Finally, a pragmatic guide for "is BEAM right for this project in 2026?"

15-1. Where BEAM Wins

15-2. Where BEAM Doesn't Win

15-3. Language Choice — Erlang vs Elixir vs gleam

              Erlang              Elixir              gleam
Types         Dynamic             Dynamic + dialyzer  Static
Syntax        Prolog-flavored     Ruby-flavored       Rust / Elm-flavored
Ecosystem     Old, stable         Most active         Growing fast
Tooling       rebar3              mix (excellent)     gleam (clean)
Sweet spot    Existing OTP systems New web / Phoenix  Type-safe greenfield
Learning      Steep due to syntax Smooth              Fast for FP folks

Recommendations:

15-4. HTTP Server Choice

15-5. Distributed Data Choice

15-6. Embedded Choice


16. Closing — BEAM Is Not Going Anywhere

Over 40 years, BEAM went from the secret weapon of a telecom company to the backbone of the internet's messaging infrastructure. In 2026, BEAM:

BEAM's promise has not changed — "systems that do not die." What was needed in a 1980s telephone exchange is still needed in a 2026 LLM inference gateway, a message queue, an IoT backend, or a real-time collaboration tool.

If your system is the kind that has to keep many connections alive at once and absolutely cannot die, BEAM deserves a serious look. Messaging, payments, IoT, chat — whatever the domain.

BEAM will probably still be quietly running somewhere in 30 years. Maybe in 50. Telephone exchanges may be gone, but the kind of problem BEAM was built for never quite goes away.


References

Comments

No comments yet.

Sign in to leave a comment