Configuration and Usage
As outlined in the Architecture chapter, the node consists of several components which are run
as separate processes. The recommended way to operate a node locally is using docker compose, which starts each
component in its own container and automatically bootstraps on first run. Operating the components without Docker
is also straightforward using the individual CLI subcommands.
This guide focuses on basic usage. To discover more advanced options we recommend exploring the various help menus
which can be accessed by appending --help to any of the commands.
Bootstrapping
The first step in starting a new Miden network is to initialize the genesis block data. This is a two-step process: first the validator signs and creates the genesis block, then the store initializes its database from that block. By default the genesis block will contain a single faucet account.
# Step 1: Validator bootstrap — create the signed genesis block and account files.
miden-node validator bootstrap \
--genesis-block-directory genesis-data \
--accounts-directory accounts
# Step 2: Store bootstrap — initialize the store database from the genesis block.
miden-node store bootstrap \
--data-directory store-data \
--genesis-block genesis-data/genesis.dat
You can also configure the account and asset data in the genesis block by passing in a toml configuration file.
This is particularly useful for setting up test scenarios without requiring multiple rounds of
transactions to achieve the desired state. Any account secrets will be written to disk inside the
the provided --accounts-directory path in the process.
miden-node validator bootstrap \
--genesis-block-directory genesis-data \
--accounts-directory accounts \
--genesis-config-file genesis.toml
The genesis configuration file should contain fee parameters, optionally a custom native faucet, optionally other fungible faucets, and also optionally, wallet definitions with assets, for example:
# The UNIX timestamp of the genesis block. It will influence the hash of the genesis block.
timestamp = 1717344256
# Defines the format of the block protocol to use for the genesis block.
version = 1
# The native faucet defaults to a MIDEN token (symbol="MIDEN", decimals=6,
# max_supply=100_000_000_000_000_000). To override it with a pre-built account
# file, specify the path:
#
# native_faucet = "path/to/faucet.mac"
#
# The path is relative to this configuration file.
# The fee parameters to use for the genesis block.
[fee_parameters]
verification_base_fee = 0
# Another fungible faucet (optional) to initialize at genesis.
[[fungible_faucet]]
# The token symbol to use for the token
symbol = "FUZZY"
# Number of decimals your token will have, it effectively defines the fixed point accuracy.
decimals = 6
# Total supply, in _base units_
#
# e.g. a max supply of `1e15` _base units_ and decimals set to `6`, will yield you a total supply
# of `1e15/1e6 = 1e9` `FUZZY`s.
max_supply = 1_000_000_000_000_000
# Storage mode of the faucet account.
storage_mode = "public"
[[wallet]]
# List of all assets the account should hold. Each token type _must_ have a corresponding faucet.
# The number is in _base units_, e.g. specifying `999 FUZZY` at 6 decimals would become
# `999_000_000`.
assets = [{ amount = 999_000_000, symbol = "FUZZY" }]
# Storage mode of the wallet account.
storage_mode = "private"
# The code of the account can be updated or not.
# has_updatable_code = false # default value
To include pre-built accounts (e.g. bridge or wrapped-asset faucets) in the genesis block, use
[[account]] entries with paths to .mac files:
[[account]]
path = "bridge.mac"
[[account]]
path = "eth_faucet.mac"
Operation
Using docker compose
Build the Docker image and start the node. Bootstrap happens automatically on first run.
The default compose-up target starts all node components along with a telemetry stack
(Tempo + Grafana) and
a network monitor:
make docker-build-node
make docker-build-monitor
make compose-genesis
make compose-up
This starts:
- All node components (store, validator, block-producer, rpc, ntx-builder) with OpenTelemetry tracing enabled
- Tempo — receives OTLP traces from the node on port
4317, HTTP API on port3200 - Grafana — pre-configured with a Tempo datasource and a Miden Node dashboard, available at
http://localhost:3000 - Network monitor — monitors the local node, available at
http://localhost:3001
Follow logs:
make compose-logs
Stop the node:
make compose-down
Teardown and regenesis:
make compose-genesis
Running components individually
A convenience script is provided that bootstraps and starts all components as separate processes:
./scripts/run-node.sh
To avoid genesis and simply restart from a previous run:
SKIP_BOOTSTRAP=true scripts/run-node.sh
Each component can also be started as a standalone process. For example:
# Start the store
miden-node store start \
--rpc.url http://0.0.0.0:50001 \
--ntx-builder.url http://0.0.0.0:50002 \
--block-producer.url http://0.0.0.0:50003 \
--data-directory /tmp/store
# Start the validator
miden-node validator start http://0.0.0.0:50101 \
--data-directory /tmp/validator
# Start the block producer
miden-node block-producer start http://0.0.0.0:50201 \
--store.url http://127.0.0.1:50003 \
--validator.url http://127.0.0.1:50101
# Start the RPC server
miden-node rpc start \
--url http://0.0.0.0:57291 \
--store.url http://127.0.0.1:50001 \
--block-producer.url http://127.0.0.1:50201 \
--validator.url http://127.0.0.1:50101
# Start the network transaction builder
miden-node ntx-builder start \
--store.url http://127.0.0.1:50002 \
--block-producer.url http://127.0.0.1:50201 \
--validator.url http://127.0.0.1:50101 \
--data-directory /tmp/ntx-builder
gRPC server limits and timeouts
The RPC component enforces per-request timeouts, per-IP rate limits, and global concurrency caps. Configure these settings with the following options:
--grpc.timeout(default10s): Maximum request duration before the server drops the request.--grpc.max_connection_age(default30m): Maximum lifetime of a connection before the server closes it.--grpc.burst_size(default128): Per-IP burst capacity before rate limiting kicks in.--grpc.replenish_per_sec(default16): Per-IP request credits replenished per second.--grpc.max_global_connections(default1000): Maximum concurrent gRPC connections across all clients.
Systemd
Our Debian packages install a systemd service which operates the node in bundled
mode. You'll still need to run the bootstrapping process before the node can be started.
You can inspect the service file with systemctl cat miden-node or alternatively you can see it in
our repository in the packaging folder. For the bootstrapping process be sure to specify the data-directory as
expected by the systemd file.
RocksDB tuning
The store uses RocksDB for the account and nullifier trees, one instance each. The two most impactful knobs per tree are exposed as CLI flags (also available as environment variables):
| Flag | Default | Notes |
|---|---|---|
--account_tree.rocksdb.max_cache_size | 2 GiB | Shared LRU block cache. Increase on memory-rich hosts. |
--account_tree.rocksdb.max_open_fds | 64 | Raise to 512+ when ulimit -n allows. |
--nullifier_tree.rocksdb.max_cache_size | 2 GiB | Same as above for the nullifier tree. |
--nullifier_tree.rocksdb.max_open_fds | 64 | Same as above for the nullifier tree. |
Compaction parallelism is set automatically to the number of available CPU cores.
miden-node store start \
--data-directory data \
--rpc.url http://0.0.0.0:57291 \
--account_tree.rocksdb.max_cache_size 4294967296 \
--account_tree.rocksdb.max_open_fds 512 \
--nullifier_tree.rocksdb.max_cache_size 4294967296 \
--nullifier_tree.rocksdb.max_open_fds 512
Environment variables
Most configuration options can also be configured using environment variables as an alternative to providing the values
via the command-line. This is useful for certain deployment options like docker, where they can be easier
to define or inject instead of changing the underlying command line options.
These are especially convenient where multiple different configuration profiles are used. Write the environment
variables to some specific profile.env file and load it as part of the node command:
source profile.env && miden-node <...>
This works well on Linux and MacOS, but Windows requires some additional scripting unfortunately.
See the .env files in each of the binary crates' directories for a list of all available environment variables.