Whichever install path you took (Docker or source), the L2 node reads its config from /etc/ogmara/ogmara.toml. This page covers the minimum blocks you must fill in to connect to the network, the optional features you can opt into for production, and the safe procedure for switching between testnet and mainnet without losing your node key.
The configuration file lives at /etc/ogmara/ogmara.toml. The Docker container auto-generates a default annotated config in your bind-mounted host directory on first start (and source builds can use ogmara-node init to do the same). Either way you only need to fill in the four blocks below to get connected.
For Docker — the file is already on your host filesystem (the bind-mount from the install step). Edit it directly and restart:
# Edit the four blocks shown below (use your favourite editor)
nano /etc/ogmara/ogmara.toml
# Apply the changes
docker restart ogmara-l2
For source builds — generate, edit, install:
# Generate a default config (the same default the docker container uses)
ogmara-node init --output /etc/ogmara/ogmara.toml
# Edit it
sudo nano /etc/ogmara/ogmara.toml
Minimum changes needed in the auto-generated config to connect to the network:
# /etc/ogmara/ogmara.toml — minimum changes from the auto-generated default
[api]
# REQUIRED for [anchoring.metadata] auto-derive and for being reachable
# from clients. Set to the public URL where this node's API is served
# (typically via the reverse proxy you configure later).
# Bracketed IPv6 is supported: "http://[2001:db8::1]:41721".
public_url = "https://node.yourdomain.com"
# If the API is reached through a reverse proxy or Docker port-forward, list the
# proxy / Docker-bridge CIDR(s) here. The node trusts X-Forwarded-For / Forwarded
# to resolve the REAL client IP ONLY from these hops (and loopback). This governs
# the admin-dashboard localhost bypass and per-IP rate limiting: if it's empty
# while you're behind a proxy, the admin localhost bypass won't recognize you
# (use wallet login instead) and rate limits bucket everyone as the proxy IP.
# Leave empty if clients hit the node directly. Example (default Docker bridge):
trusted_proxies = ["172.17.0.0/16"]
[klever]
# Klever mainnet endpoints
node_url = "https://node.mainnet.klever.org"
api_url = "https://api.mainnet.klever.org"
# Ogmara smart contract address on Klever mainnet
contract_address = "klv1qqqqqqqqqqqqqpgq8c9yag9vuc2pe64fwvqsq9e8ul8w5zuglf5qfgh7z3"
The auto-generated default exposes every operator-tunable knob with inline comments — on-chain state anchoring ([anchoring]), on-chain peer-discovery publication ([anchoring.metadata]), staleness filters, media cache tuning, alert dispatchers (Telegram / Discord / webhook), and snapshot sync. Browse the full annotated reference on GitHub: ogmara.example.toml. See also § Optional Production Features below.
For testing and development, use Klever testnet: change the URLs to https://node.testnet.klever.org and https://api.testnet.klever.org, and use the testnet contract address klv1qqqqqqqqqqqqqpgq0ja2j7xwz843ryfsk9vlz6xzsaak590h6pgq7nwr02.
A bare node connects to the network and serves your users. To participate more fully — help anchor chain state, appear in the network's peer-discovery registry, get paged when something goes wrong — opt into these features in your ogmara.toml. All are off by default; flip them on once you understand the trade-offs.
| Feature | Config section | What it does |
|---|---|---|
| State anchoring | [anchoring] |
Periodically submits a Merkle root of your node's L2 state to the Ogmara smart contract on Klever. Other nodes verify against your submissions; reaching quorum makes a root canonical. Requires KLV in the anchorer wallet for transaction fees. |
| On-chain peer discovery | [anchoring.metadata] |
Publishes your node's libp2p multiaddrs on chain so other nodes can discover you without relying on a hardcoded bootstrap list. Auto-derives from [api] public_url + [network] listen_port (IPv4, IPv6, or DNS). Strongly recommended — non-publishers still anchor normally, but they appear on the public network dashboard at ogmara.org/network.html as (no metadata published) because the page can't derive a reachable API endpoint without it. |
| Off-chain presence gossip | [network.presence] |
v0.48.0+. Broadcasts your peer_id, public_url, and version on a libp2p gossipsub topic so the public network page can list you without on-chain registration — useful for service-provider operators who run an API but don't want to commit to anchoring economics. Independent of [anchoring]. The block isn't auto-added to configs generated by pre-v0.48.0 binaries; if you're upgrading, append it manually (see Troubleshooting). Set enabled = true and restart. |
| Graceful pause on shutdown | [anchoring] pause_on_shutdown |
SIGTERM handler signs and broadcasts a pauseNode transaction before exit so other nodes stop counting yours toward quorum during planned downtime. Opt-in because it requires the wallet key in process memory for shutdown signing. |
| Alert dispatch | [alerts], [alerts.telegram], [alerts.discord], [alerts.webhook] |
Push critical / warning / info alerts (disk usage, divergence, sync lag, etc.) to Telegram, Discord, or a generic webhook. Bot tokens and webhook URLs should be loaded from environment variables, not the config file. |
| Snapshot sync | [snapshot] |
Default-on. Fresh nodes bootstrap by fetching a peer-served Merkle-rooted state snapshot rather than replaying millions of Klever blocks. Disable serving on resource-constrained nodes if needed. |
| Peer staleness filter | [network.discovery] |
Tune how aggressively the node drops dial candidates whose last on-chain anchor is older than N days. Default 7. Local dev deployments may want a longer threshold. |
Each section is fully documented with inline comments in the auto-generated config and in the GitHub reference: ogmara.example.toml. After editing the config, restart the node (docker restart ogmara-node or sudo systemctl restart ogmara-node) for changes to take effect — the L2 node reads its config once at startup.
Testnet and mainnet nodes run on isolated networks — they cannot peer with each other. The node auto-detects which network it belongs to from the Klever URLs in your config. If you started with testnet and want to switch to mainnet (or vice versa), you must wipe the data directory because testnet and mainnet have separate channel IDs, chain state, and peer lists that would collide if mixed.
The data directory contains your node's private key — this IS your node's wallet address. Deleting it without a backup means any KLV funds on that address are permanently lost and your anchoring authorization must be re-established. Always export the key before deleting data.
# --- Source / systemd install ---
# 1. Stop the node
sudo systemctl stop ogmara-node
# 2. BACK UP YOUR NODE KEY (critical!)
ogmara-node export-key -o my-node-key.bak
# 3. Edit ogmara.toml — switch Klever URLs and contract address
# 4. Wipe the data directory (path depends on your `[node] data_dir`)
sudo rm -rf /var/lib/ogmara/data
# 5. Start the node
sudo systemctl start ogmara-node
# 6. Restore your key (keeps the same wallet address)
ogmara-node import-key -i my-node-key.bak
# Then restart to apply the restored key
sudo systemctl restart ogmara-node
For Docker installations (using the host-bind-mounted config and data dirs from the install step):
# 1. Back up the key first
docker exec ogmara-l2 ogmara-node export-key -o /data/node-key.bak
cp /var/lib/ogmara/data/node-key.bak ~/my-node-key.bak
# 2. Stop + remove the container (host config + data dirs stay intact)
docker stop ogmara-l2
docker rm ogmara-l2
# 3. Wipe the chain/messages data on the host (your host config stays
# put). The directory itself stays — we just clear its contents so the
# container can repopulate fresh state for the new network.
sudo rm -rf /var/lib/ogmara/data/*
sudo chown $(id -u):$(id -g) /var/lib/ogmara/data
# 4. Edit the host config to point at the new network
nano /etc/ogmara/ogmara.toml
# Swap node.testnet.klever.org ↔ node.mainnet.klever.org,
# api.testnet.klever.org ↔ api.mainnet.klever.org,
# and the contract address — testnet uses
# klv1qqqqqqqqqqqqqpgq0ja2j7xwz843ryfsk9vlz6xzsaak590h6pgq7nwr02
# mainnet uses
# klv1qqqqqqqqqqqqqpgq8c9yag9vuc2pe64fwvqsq9e8ul8w5zuglf5qfgh7z3
# 5. Recreate the container (same bind mounts)
docker run -d --name ogmara-l2 --restart unless-stopped \
--user $(id -u):$(id -g) \
-p 41720:41720/tcp -p 41720:41720/udp -p 41721:41721/tcp \
-v /var/lib/ogmara/data:/data -v /etc/ogmara:/etc/ogmara \
ogmara/ogmara:l2-node-latest
# 6. Restore the key
cp ~/my-node-key.bak /var/lib/ogmara/data/node-key.bak
docker exec ogmara-l2 ogmara-node import-key -i /data/node-key.bak
docker restart ogmara-l2
Messages and chain state rebuild automatically from the network. Only the private key needs to be backed up — everything else re-syncs via the chain scanner and peer sync protocol.