Skip to main content

Set up Execution Events

This guide walks you through configuring your Monad node to enable the Execution Events system, which allows you to build high-performance applications that receive lowest-latency event data via shared memory.

Prerequisites

Why execution events?

Monad produces blocks every 400 milliseconds and can process thousands of transactions per block. Traditional JSON-RPC and WebSocket APIs may not keep up with this data volume. Execution events provide the highest-throughput, lowest-latency option for consuming real-time blockchain data.

For more details, see the Execution Events documentation.

Step 1: Install required packages

Install the hugeadm utility for managing huge pages:

sudo apt install libhugetlbfs-bin
Behind the scenes

Installs the libhugetlbfs-bin package, which provides the hugeadm command-line tool. This tool manages "huge pages" - large memory pages (2MB instead of the standard 4KB) that reduce memory management overhead and improve performance for applications that need to share large amounts of data quickly, like execution events.

If you plan to write applications using the Execution Events SDK (C, C++, or Rust), also install these development packages:

sudo apt install libhugetlbfs-dev libhugetlbfs0 libzstd-dev
Behind the scenes
  • libhugetlbfs-dev: Header files for developing applications that use huge pages
  • libhugetlbfs0: Runtime library for huge page support
  • libzstd-dev: Zstandard compression library, used for efficient data encoding in the SDK

Step 2: Set up the hugetlbfs mount

The execution events system uses huge pages for high-performance shared memory communication. You need to create a persistent hugetlbfs mount.

Create a systemd service for persistent mounts

Create the service file:

sudo nano /etc/systemd/system/events-hugepages-mounts.service

Paste the following contents (replace monad with your user if different):

[Unit]
Description=Create hugepage mounts for monad
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/bin/hugeadm --create-user-mounts monad
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Behind the scenes

This systemd service runs hugeadm --create-user-mounts monad at boot time, which:

  • Creates a hugetlbfs filesystem mount at /var/lib/hugetlbfs/user/monad/
  • Sets up separate directories for different page sizes (2MB and 1GB)
  • Ensures the monad user has permission to create files in these directories

The Type=oneshot means it runs once at startup, and RemainAfterExit=yes keeps the service marked as "active" so systemd knows the mount is still needed.

Save and exit (Ctrl+O, Enter, Ctrl+X).

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable --now events-hugepages-mounts
Behind the scenes
  • daemon-reload: Tells systemd to re-read all service files, picking up your new service
  • enable --now: Enables the service to start at boot AND starts it immediately

After this, the hugepage mount will be available now and on every reboot.

Step 3: Create the event-rings directory

The execution daemon writes event data to files in this directory:

sudo mkdir -p /var/lib/hugetlbfs/user/monad/pagesize-2MB/event-rings
sudo chown monad:monad /var/lib/hugetlbfs/user/monad/pagesize-2MB/event-rings
Behind the scenes
  • mkdir -p: Creates the event-rings directory (and any parent directories if needed)
  • chown monad:monad: Changes ownership to the monad user so the execution daemon can write event ring files here

The path /var/lib/hugetlbfs/user/monad/pagesize-2MB/ is where hugeadm created the 2MB huge page mount. The execution daemon will create shared memory files in event-rings/ that your consumer applications can read.

note

Replace monad with your actual user if you're running the node as a different user.

Step 4: Configure the Execution daemon

Create a systemd override to enable execution events output.

Open the override editor:

sudo systemctl edit monad-execution
Behind the scenes

Opens a text editor to create an "override" file for the monad-execution service. Overrides let you modify service settings without editing the original unit file (which would be overwritten on package updates). The override file is saved at /etc/systemd/system/monad-execution.service.d/override.conf.

Add the following configuration:

[Service]
ExecStart=
ExecStart=/usr/local/bin/monad \
--chain "$CHAIN" \
--db /dev/triedb \
--block_db /home/monad/monad-bft/ledger \
--statesync /home/monad/monad-bft/statesync.sock \
--exec-event-ring /var/lib/hugetlbfs/user/monad/pagesize-2MB/event-rings/monad-exec-events \
--sq_thread_cpu 1 \
--log_level INFO
Behind the scenes
  • ExecStart= (empty): Clears the original command from the base unit file (required for overrides)
  • ExecStart=/usr/local/bin/monad ...: Sets the new command with all flags
  • --exec-event-ring: The key flag that enables execution events output. It specifies the path where the daemon will create the shared memory event ring file

The other flags are standard execution daemon options copied from the default configuration.

warning

Both ExecStart= lines are required. The first clears the original value, and the second sets the new value with the --exec-event-ring flag.

Reload and restart the Execution daemon:

sudo systemctl daemon-reload
sudo systemctl restart monad-execution
Behind the scenes
  • daemon-reload: Tells systemd to re-read all service files, including your new override
  • restart monad-execution: Stops and starts the execution daemon with the new configuration

After restart, the daemon will begin writing execution events to the shared memory ring buffer.

Step 5: Configure the RPC daemon for WebSocket support

This step enables WebSocket support in the RPC server, allowing you to use eth_subscribe for real-time data.

warning

RPC with WebSocket enabled has a hard dependency on Execution running with events enabled. If you start RPC with --ws-enabled before completing Step 4, RPC will crash.

Open the RPC override editor:

sudo systemctl edit monad-rpc
Behind the scenes

Creates an override file at /etc/systemd/system/monad-rpc.service.d/override.conf to customize the RPC service configuration.

Add the following configuration:

[Service]
ExecStart=
ExecStart=/usr/local/bin/monad-rpc \
--ipc-path /home/monad/monad-bft/mempool.sock \
--triedb-path /dev/triedb \
--otel-endpoint "http://0.0.0.0:4317" \
--allow-unprotected-txs \
--node-config /home/monad/monad-bft/config/node.toml \
--exec-event-path /var/lib/hugetlbfs/user/monad/pagesize-2MB/event-rings/monad-exec-events \
--ws-enabled
Behind the scenes
  • ExecStart= (empty): Clears the original command from the base unit file (required for overrides)
  • --exec-event-path: Points to the same event ring file that the Execution daemon writes to
  • --ws-enabled: Enables WebSocket support on port 8081 (default)

Reload and restart the RPC daemon:

sudo systemctl daemon-reload
sudo systemctl restart monad-rpc
Behind the scenes
  • daemon-reload: Tells systemd to re-read all service files, including your new override
  • restart monad-rpc: Stops and starts the RPC daemon with WebSocket support enabled

Step 6: Verify the setup

Check that the event ring file has been created:

ls -la /var/lib/hugetlbfs/user/monad/pagesize-2MB/event-rings/
Behind the scenes

Lists the contents of the event-rings directory. You should see a file named monad-exec-events. This is the shared memory file that the execution daemon writes to and your consumer applications read from.

You should see a file named monad-exec-events (or similar).

Check the Execution daemon status:

sudo systemctl status monad-execution
Behind the scenes

Shows the current status of the execution daemon service. Look for:

  • Active: active (running) - the service is running
  • No error messages in the recent log output

If you see errors related to the event ring or hugepages, check the Troubleshooting section below.

The service should be active and running without errors.

Check WebSocket connectivity

A quick way to check if WebSocket connectivity is working is to use websocat, a command-line WebSocket client. It can be installed via cargo install websocat or by downloading precompiled binaries.

Run it in verbose mode with the WebSocket service on default port 8081:

websocat -v ws://localhost:8081

You should see output like:

[INFO websocat::lints] Auto-inserting the line mode
[INFO websocat::stdio_threaded_peer] get_stdio_peer (threaded)
[INFO websocat::ws_client_peer] get_ws_client_peer
[INFO websocat::net_peer] Connected to TCP 127.0.0.1:8081
[INFO websocat::ws_client_peer] Connected to ws
[INFO websocat::ws_peer] Received WebSocket ping

To subscribe to new block headers, type this JSON-RPC call and press enter:

{ "id": 1, "jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newHeads"] }

Every half-second or so, you should see updates about new blocks.

Next steps

With execution events and WebSocket support enabled, you can now:

  • Build your own data consumer: Write high-performance applications in C, C++, or Rust using the Execution Events SDK. See the Getting Started guide for examples.

  • Learn more about WebSocket subscriptions: See the WebSocket Guide for all available subscription types.

Troubleshooting

Event ring file not created

Ensure the hugepage mount is active:

mount | grep hugetlbfs
Behind the scenes

Lists all mounted filesystems and filters for hugetlbfs mounts. You should see output like:

hugetlbfs on /var/lib/hugetlbfs/user/monad/pagesize-2MB type hugetlbfs (rw,relatime,...)

If there's no output, the hugepage mount isn't active.

You should see a mount point like /var/lib/hugetlbfs/user/monad/pagesize-2MB.

If not, restart the hugepages service:

sudo systemctl restart events-hugepages-mounts
Behind the scenes

Re-runs the hugepage mount service, which will recreate the hugetlbfs mount points. After this, run the mount | grep hugetlbfs command again to verify.

Execution daemon fails to start

Check the logs for errors:

sudo journalctl -u monad-execution -f
Behind the scenes
  • journalctl -u monad-execution: Shows logs for the execution daemon service
  • -f: Follows the log in real-time (like tail -f)

Press Ctrl+C to stop following. Look for error messages mentioning the event ring path, permissions, or hugepages.

Common issues:

  • The event-rings directory doesn't exist or has wrong permissions
  • The hugepage mount is not available
  • Incorrect paths in the systemd override

Permission denied errors

Ensure the event-rings directory is owned by the correct user:

sudo chown -R monad:monad /var/lib/hugetlbfs/user/monad/
Behind the scenes

Recursively changes ownership of the entire hugepage directory tree to the monad user. The -R flag ensures all subdirectories and files are updated.

Further resources