Skip to content

Nextify Load Balancer

Protocol Studio

Included with every load balancer

Load balancing that understands your protocol. Describe your bytes once, as a pattern, and the Nextify Load Balancer checks health, weighs servers and routes connections by the fields inside, counts those fields live, and gives you a debugger for real traffic. Every packet is read in the kernel at native speed.

In every load balancer, at no extra cost. Open a load balancer in the portal and choose its Protocol Studio tab. The examples on this page run in your browser on sample data: no load balancer, no traffic, nothing sent anywhere.

Bring your own protocol

Game servers, IoT devices, custom RPC, anything with bytes on the wire. Describe its messages in a pattern: fixed fields, lengths, enums, nested structs.

Health checks that understand it

Send your own request, read the reply with the pattern, and decide with a condition on its fields, not just “the port answered”.

Every packet, at kernel speed

Patterns compile to eBPF, which the kernel verifies and JIT-compiles, so every packet is decoded in the kernel at native speed. What does not compile still works on the pattern interpreter, automatically, and the editor shows which parts run where.

Live field metrics

Count any field on every packet: how often each value is seen, histograms over buckets, rates, sums and averages, live in the portal.

Fields as weights

Show parsed numbers on the health page and let them set a server's weight, so busier servers get fewer new connections.

Route on fields

Send a flow to a pool by the fields of its first message: at fixed offsets, after a length, behind a condition. Rules run natively, in the kernel for UDP and in HAProxy for TCP.

Messages across packets

A message split over several datagrams or TCP segments is reassembled before it is decoded. You choose where a message ends: where the pattern ends, a delimiter or a size field.

Field rules

A protocol firewall, rate limits and bans keyed by fields, sticky sessions by a payload id, canary splits, mirroring, in-place rewrites and latency by request id: in the kernel, for TCP and UDP listeners.

Trace and debug, safely

Watch decoded traffic, set breakpoints with conditions, hold one flow and step through it while everything else keeps flowing.

No small limits

Distinct values counted, message sizes and flows in progress are settings, sized from the load balancer's memory; the editor shows what each one takes.

An editor that helps

In the portal: highlighting, completion, linting as you type, a Test button on captured or live samples, and starter templates.

Try it

Read a reply, field by field

A game server's status reply, described with a pattern. Pick a field to see its bytes, pick a byte to see its field, and switch the sample to watch the health check and the server's weight follow.

game-status.hexpatSimulation · sample data

Pattern

  1. #pragma endian big
  2. ​
  3. enum State : u8 {
  4. Starting = 0,
  5. Ready = 1,
  6. Draining = 2
  7. };
  8. ​
  9. struct Status {
  10. u32 magic;
  11. u8 version;
  12. State state;
  13. u16 players;
  14. u16 max_players;
  15. u8 region;
  16. u8 name_len;
  17. char name[name_len];
  18. };
  19. ​
  20. Status status @ 0x00;

Reply · 18 bytes

000000004E584C4201010025004002066672612D
000000103031

Parsed

Hover or tap a field to see its bytes and its line; move through the bytes with the arrow keys.

Health check

  1. status.magic == 0x4E584C42
  2. && status.state == State::Ready
  3. && status.players < status.max_players
Sample reply
  • status.magic == 0x4E584C42status.magic is 0x4E584C42
  • status.state == State::Readystatus.state is Ready
  • status.players < status.max_players37 < 64
game-2 · 10.0.4.12:27015 · up, in rotationweight 42 of 100: a fuller server gets fewer new players

A healthy game server with free slots. weight = 100 * (status.max_players - status.players) / status.max_players

Route on fields

Send each connection where its bytes say

RuleIf status.region == 2 → pool eu
First packet, region =

Matches: the connection goes to pool eu.

TCP listener · HAProxy payload ACL
tcp-request inspect-delay 2s
tcp-request content accept if { req.len ge 11 }
acl nx_region_eu req.payload(10,1) -m bin 02
use_backend eu if nx_region_eu
UDP listener · nftables payload match + IPVS
udp dport 27015 @th,144,8 0x02 meta mark set 0x2
ipvsadm -A -f 2 -s rr    # fwmark 2 = pool eu

Compiled, never interpreted on the hot path. A field at a fixed offset (here byte 10) becomes a native match; the first message decides and the connection stays on its pool. On UDP, a field after a length, a loop or a condition is matched by the pattern's inline program (eBPF) in the kernel instead. The rare construct that cannot run in the kernel still works for health checks, and the editor shows the line that keeps it from routing.

Trace and debug

Pause one connection, not your service

A live view of decoded packets, with breakpoints on addresses and fields. Field conditions are checked in the kernel on every message, so a hold holds the very packet that matches and a capture copies exactly the packets you asked for. Only that client's flow waits in the inspector; Continue, Step through it packet by packet, or Drop a packet. Below, a simulated stream from documentation addresses.

trace · udp/27015 · pool gameSimulation · made-up traffic
client == 203.0.113.7 && msg.type == Login0 hits
Press Play to start the simulated stream.

Inspector

No flow held. When a packet matches the breakpoint, only that client's connection waits here; the rest of the stream keeps going.

  1. enum Type : u8 {
  2. Hello = 1, Login = 2, Move = 3,
  3. Chat = 4, Ping = 5
  4. };
  5. ​
  6. struct Msg {
  7. Type type;
  8. u16 seq;
  9. u16 len;
  10. u8 data[len];
  11. };
  12. ​
  13. Msg msg @ 0x00;

Field rules

Act on every message, in the kernel

The pattern that decodes your traffic also enforces your rules: drop, limit, ban, route, mirror and rewrite by the fields inside each message, and measure request to response latency by an id. Rules are part of the configuration, staged and versioned like every change, with live counters for each.

RuleActs onThen
Strict modeinvalid messagesDrop what does not parse or grows past the message size
Forbidden opcodemsg.op == 9Drop, and ban a source after 20 in 60 s
Per-player limitclient + msg.player_id20 per second, burst 40; drop the rest
Ranked canarymsg.mode == 110 % of players to the new build's pool
Sessionsmsg.session_idKeep each session on one server, across address changes
Round tripmsg.seq, by msg.opp50 / p95 / p99 per message type; alert above 50 ms

An example on a UDP game listener. Every rule runs in the kernel on each complete message, with live counters per rule.

A protocol firewall

Drop messages whose fields you forbid: opcodes, oversized length fields, wrong versions. Strict mode drops whatever fails to parse.

Rate limits by field

A token bucket per player id, API key or session, optionally per client address too. Over the limit: drop or count. The busiest keys are listed live.

Automatic bans

Sources that keep hitting a rule within a window are banned in the kernel and on the host, like every other ban, with the rule as the reason.

Sticky by a field

Consistent hashing of a session id carried in the payload: mobile clients and NAT rebinding keep their server, and a server leaving moves only its own keys.

Canary splits

Send a stable share of keys to another pool, or pin chosen keys to it, while the rest stay where they are.

Mirror real traffic

Copy matching messages to a test pool at a capped rate; its replies are discarded, so a new version sees production traffic without answering it.

Rewrite in place

Set a field to a new value of the same length, with the checksum fixed in the kernel: stamp a region, mask an identifier.

Latency by request id

Pair each request with its response by an id field and see percentiles per message type, live.

Alerts

Thresholds on any rule counter or field metric (a drop rate, a share of limited messages, a p95) show up as notifications in the portal and in the health history.

For TCP and UDP listeners. HTTP, HTTPS and TLS listeners terminate in HAProxy, where routing, rate limits and bans have their own settings. On TCP a dropped message resets the connection (dropping one segment would stall the stream), and a connection's server is picked before its first byte, so canary splits and sticky routing by a field are done by HAProxy on fields at fixed offsets. A rule the kernel cannot run is refused with the line and the reason when you save; nothing is skipped silently.

Safe on live traffic

  • Decoding in the kernel never drops or delays a packet: the kernel verifies each program before it runs (it always ends and only reads), and whatever the program does not cover runs on the interpreter, off the forwarding path.
  • Precise copies, capped: only messages that match your conditions are copied to the trace, through a fixed-size buffer; when it is full, copies are dropped, never traffic.
  • Opt-in and time-limited: a trace session is started by you, logged in your account's audit log and ends by itself after 15 minutes.
  • A held flow continues by itself after 60 seconds; at most 3 flows are held at once, and a hold never blocks other connections, health checks or bans.
  • Nothing is stored unless you save a sample, and you can delete it. Nobody outside your account ever sees your traffic.
  • Field rules are checked before they run: a rule the kernel cannot execute is refused when you save, with the line that prevents it. Mirrors are rate-capped and their replies discarded; trusted sources are never rate-limited or banned.
  • Patterns run sandboxed, with limits on CPU time, memory and reply size, and no access to files or the network.

Get started

  1. Create a load balancer, or open one you have, in the portal.
  2. Choose its Protocol Studio tab and start a pattern from a template: a length-prefixed TCP header, Minecraft server list ping, MQTT, Redis, DNS or a custom game protocol.
  3. Run it on a sample (pasted hex, a file, or a live probe of one of your servers), then use it for a pool's health check, a routing rule, or a live trace.

The standard health checks stay as they are: HTTP (method, path, expected status), TCP connect, and TCP or UDP send-and-expect with text, hex or a regular expression. Protocol Studio adds to them, in the same configuration, versioned and checked like every other change.

Built on open source

Powered by PatternLanguage

Protocol Studio reads your bytes with PatternLanguage, the pattern language of the ImHex hex editor, written by WerWolv and its contributors and released under the LGPL-2.1 license. On a load balancer it runs in its own sandboxed helper process.

  • Patterns you write in ImHex work here, and the ones you write here open in ImHex (functions that read files or the environment are switched off on a load balancer).
  • The community collection ImHex-Patterns is a good starting point for formats that already have a pattern.

PatternLanguage, ImHex and ImHex-Patterns are independent open-source projects. Nextify is not affiliated with them, and they do not endorse Nextify.