XEQM Service Node Operator Guide: Startup & Registration
Step-by-step guide to running an XEQM service node. Spin up the daemon with Docker, fund the operator stake, generate your registration command, and start earning rewards. Solo and pool node setups covered.
TL;DR: To run an XEQM service node, you start the XEQM daemon (easiest via the official Docker image), let it sync, fund a wallet with the operator stake (200,000 XEQM solo or 100,000 XEQM minimum for a pool node), generate a registration command from the daemon, and submit it through the wallet CLI. Once your node shows
active: trueandpayable: true, you earn rewards. Budget 30 to 60 minutes for a clean first-time setup.
Official docs: This guide is also maintained in the XEQMLabs documentation. For the always-current version, see the Service Node Operator Guide.
If you can run a Docker container and copy/paste a few commands, you can run a service node.
Self-built binaries: This guide uses the official XEQM Docker image, which is the simplest path for most operators. If you run a self-built binary instead, the steps are similar. You skip the Docker parts, start the daemon directly with the same flags, point the RPC commands at your daemon’s port, and the registration flow is identical.
What you’ll need
- A VPS with a stable internet connection (24/7 uptime expected)
- 8 GB RAM, 2+ CPU cores, 200 GB disk recommended (smaller works for now, but you’ll grow into it)
- A public IP address reachable from the internet on port 9230 (P2P)
- Docker installed (Docker Desktop on Windows, Docker Engine on Linux)
- A funded XEQM wallet with at least 200,000 XEQM plus a small amount for the registration fee
Most operators run on a small VPS such as DigitalOcean, Hetzner, or Vultr.
Port reference
The XEQM daemon uses these ports. You’ll see them throughout this guide:
| Port | Purpose | Exposure |
|---|---|---|
| 9230 | P2P (peer-to-peer) | Public, must be reachable from the internet |
| 9231 | Admin RPC | Local only, never expose publicly |
| 9232 | Quorumnet (SN-to-SN consensus) | Public, must be reachable from other SNs |
| 9233 | Public OMQ (optional) | Public if enabled |
| 9234 | Wallet RPC | Local only |
Make sure your firewall allows inbound traffic on 9230 and 9232.
Step 1: Start your service node daemon
Pick the section for your operating system.
Linux
Create a folder for your SN’s data:
mkdir -p ~/xeqm-sn01
cd ~/xeqm-sn01
Stop and remove any existing container (in case you’re re-running):
docker stop sn01 2>/dev/null
docker rm sn01 2>/dev/null
Pull the latest image:
docker pull ghcr.io/xeqmlabs/xeqm-node:v1.0.3
Start the daemon. Replace YOUR_PUBLIC_IP with your server’s actual public IP (find it with curl ifconfig.me):
docker run -d \
--name sn01 \
--restart unless-stopped \
--network host \
-v "$(pwd)/data:/data" \
ghcr.io/xeqmlabs/xeqm-node:v1.0.3 \
--service-node \
--data-dir=/data \
--p2p-bind-ip=0.0.0.0 \
--p2p-bind-port=9230 \
--rpc-admin=127.0.0.1:9231 \
--service-node-public-ip=YOUR_PUBLIC_IP \
--quorumnet-port=9232 \
--add-priority-node=seed-1.xeqmlabs.com:9230 \
--log-level=2 \
--non-interactive
Watch the logs to confirm it’s syncing:
docker logs -f sn01
You should see peer connections forming and blocks being processed. Press Ctrl+C to exit the log view. The container keeps running.
Windows (Docker Desktop / PowerShell)
Make sure Docker Desktop is running (whale icon in your system tray, not animating). Then in PowerShell:
Create a folder for your SN’s data:
mkdir C:\xeqm-sn01
cd C:\xeqm-sn01
Stop and remove any existing container:
docker stop sn01; docker rm sn01
Pull the latest image:
docker pull ghcr.io/xeqmlabs/xeqm-node:v1.0.3
Start the daemon. Replace YOUR_PUBLIC_IP with your machine’s actual public IP:
docker run -d `
--name sn01 `
--restart unless-stopped `
-p 9230:9230 `
-p 127.0.0.1:9231:9231 `
-p 9232:9232/udp `
-v "${PWD}/data:/data" `
ghcr.io/xeqmlabs/xeqm-node:v1.0.3 `
--service-node `
--data-dir=/data `
--p2p-bind-ip=0.0.0.0 `
--p2p-bind-port=9230 `
--rpc-admin=0.0.0.0:9231 `
--service-node-public-ip=YOUR_PUBLIC_IP `
--quorumnet-port=9232 `
--add-priority-node=seed-1.xeqmlabs.com:9230 `
--log-level=2 `
--non-interactive
Why the asymmetric port settings? On Windows we can’t use host networking like on Linux. The 127.0.0.1:9231:9231 mapping keeps the admin RPC bound to your host’s loopback only, so it is not reachable from the public internet. Setting --rpc-admin=0.0.0.0:9231 lets the daemon inside the container accept the connection that Docker forwards in. If you bind the daemon to 127.0.0.1 instead, Docker’s port forwarding can’t reach it and your RPC calls will fail.
Watch the logs:
docker logs -f sn01
Step 2: Wait for the daemon to sync
Before you can register, the daemon needs to sync the chain. Check progress:
Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc \
-X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' \
| jq '.result | {height, target_height, status_line}'
Windows (PowerShell)
$result = Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"0","method":"get_info"}'
$result.result | Select-Object height, target_height, status_line
Wait until height matches target_height (or target_height is 0, meaning the network thinks you’re synced). The status_line field also tells you the current state. This typically takes 15 to 60 minutes depending on your connection and the chain length.
Step 3: Fund your operator wallet
A solo (full) operator stake is 200,000 XEQM. A pool operator stake is a minimum of 100,000 XEQM, with the remaining 100,000 filled from up to 10 community contributors.
Send the appropriate amount to your XEQM wallet and wait for it to become unlocked (10 confirmations). Verify in your wallet CLI:
balance
You should see Unlocked balance: showing at least the operator stake plus a small fee. (New to XEQM? See how to buy XEQM.)
Step 4: Generate your registration command
This step queries your daemon for its public key and signs a registration with your wallet address as the operator. You have two options: a solo (full) node or a pool node.
Operator cut (mainnet): The operator cut is capped at 10% maximum. The commands below use
operator_cut: "0"as the default, so you can raise it anywhere from"0"to"10"for the fee you want to keep on top of your staked share. For solo nodes the cut doesn’t matter (you’re the only contributor either way). For pool nodes it’s the fee you take from your contributors before reward distribution.
Option A: Solo node (you stake the full 200,000 XEQM)
Use contributor_amounts: [200000000000000] (200,000 XEQM in atomic units).
Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_service_node_registration_cmd","params":{"operator_cut":"0","contributor_addresses":["YOUR_WALLET_ADDRESS"],"contributor_amounts":[200000000000000],"staking_requirement":200000000000000}}' \
| jq -r '.result.registration_cmd'
If jq isn’t installed:
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_service_node_registration_cmd","params":{"operator_cut":"0","contributor_addresses":["YOUR_WALLET_ADDRESS"],"contributor_amounts":[200000000000000],"staking_requirement":200000000000000}}' \
| grep -o '"registration_cmd":"[^"]*"' | cut -d'"' -f4
Windows (PowerShell)
$result = Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"0","method":"get_service_node_registration_cmd","params":{"operator_cut":"0","contributor_addresses":["YOUR_WALLET_ADDRESS"],"contributor_amounts":[200000000000000],"staking_requirement":200000000000000}}'
$result.result.registration_cmd
Option B: Pool node (operator stakes the minimum, contributors fill the rest)
Use contributor_amounts: [100000000000000] (100,000 XEQM operator minimum). The remaining 100,000 XEQM gets filled by up to 10 community contributors after registration. To take an operator fee on top of your stake, raise operator_cut from "0" up to a maximum of "10".
Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_service_node_registration_cmd","params":{"operator_cut":"0","contributor_addresses":["YOUR_WALLET_ADDRESS"],"contributor_amounts":[100000000000000],"staking_requirement":200000000000000}}' \
| jq -r '.result.registration_cmd'
Windows (PowerShell)
$result = Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"0","method":"get_service_node_registration_cmd","params":{"operator_cut":"0","contributor_addresses":["YOUR_WALLET_ADDRESS"],"contributor_amounts":[100000000000000],"staking_requirement":200000000000000}}'
$result.result.registration_cmd
Replace these placeholders
In each command above, replace YOUR_WALLET_ADDRESS with your XEQM wallet address (it starts with XEQM).
Expected output
A long single-line string that looks like this:
register_service_node 0 XEQMxxxxxxxx... 200000000000000 1786xxxxxx <pubkey> <signature>
Copy the entire line, including all spaces. Don’t add line breaks.
Step 5: Submit the registration via your wallet CLI
Open your XEQM wallet CLI (xeqm-wallet-cli), make sure your wallet is open and unlocked, then paste the registration command exactly as you copied it. The wallet will prompt you to confirm the operator stake. Type Yes and hit Enter.
Wait for the registration transaction to be included in a block (usually 1 to 2 minutes). First, get your SN’s pubkey:
# Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_service_node_key"}' | jq -r '.result.service_node_pubkey'
# Windows (PowerShell)
(Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"0","method":"get_service_node_key"}').result.service_node_pubkey
Then confirm your node’s state:
# Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_service_nodes"}' \
| jq '.result.service_node_states[] | select(.service_node_pubkey == "YOUR_SN_PUBKEY") | {active, funded, payable, registration_height}'
# Windows (PowerShell)
$body = '{"jsonrpc":"2.0","id":"0","method":"get_service_nodes"}'
$result = Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body $body
$result.result.service_node_states | Where-Object { $_.service_node_pubkey -eq "YOUR_SN_PUBKEY" } | Select-Object active, funded, payable, registration_height
Once your SN shows active: true and payable: true (after a 4-block grace period), you’re earning rewards.
Day-to-day operations
Check your SN’s health
# Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' \
| jq '.result.status_line'
# Windows (PowerShell)
(Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"0","method":"get_info"}').result.status_line
You’ll see a status line like this:
v23.x.x; Height: 1234, SN: active, proof: 2m3s
proof: 2m3s means your last uptime proof was accepted 2 minutes 3 seconds ago. Healthy is anything under 2 minutes. Over 30 minutes risks decommission.
Restart the daemon
docker restart sn01
Update to the latest image version
docker stop sn01
docker rm sn01
docker pull ghcr.io/xeqmlabs/xeqm-node:v1.0.3
# Then re-run the docker run command from Step 1
Your registration, keys, and chain data persist in the data/ folder, and only the daemon binary updates. As long as you keep that folder mounted with -v "$(pwd)/data:/data", you’re fine.
Troubleshooting
”Connection closed” or “Unable to connect” on RPC calls
Your daemon was started with --rpc-admin=127.0.0.1:9231 instead of --rpc-admin=0.0.0.0:9231, or your port mapping doesn’t match. On Windows especially, Docker’s port forwarding can’t reach a service bound to the container’s internal loopback.
Quick workaround: run the RPC from inside the container, which bypasses port forwarding:
docker exec sn01 curl -s http://127.0.0.1:9231/json_rpc \
-X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}'
Proper fix: recreate the container with --rpc-admin=0.0.0.0:9231 (Windows) or --rpc-admin=127.0.0.1:9231 (Linux with host networking).
”No operator contribution given” / “Failed to make registration command”
Double-check your contributor_amounts value. It must be in atomic units (1 XEQM = 1,000,000,000 atomic units), so 200,000 XEQM is 200000000000000 with no commas and no decimals. Also check that your wallet address starts with XEQM and contains no whitespace.
My SN shows active: false or payable: false
funded: false means the registration TX hasn’t confirmed yet, or your operator stake hasn’t fully committed. Wait a few blocks.
active: true, payable: false is normal for the first 4 blocks after activation. The chain enforces a brief grace period before rewards begin.
active: false with decommission_count > 0 means your SN was decommissioned for missing too many uptime proofs. Restart the daemon, fix any networking issues, and the chain will auto-recommission you within about 30 minutes once proofs resume.
”Last uptime proof” is more than 30 minutes ago
Your SN is at risk of decommission. Common causes are a crashed or stopped daemon (check docker ps), a firewall blocking port 9230 or 9232, or a --service-node-public-ip that doesn’t match your actual public IP.
Fix the underlying issue and submit a fresh proof:
# Linux / Mac
curl -s http://127.0.0.1:9231/json_rpc -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"0","method":"send_uptime_proof"}'
# Windows (PowerShell)
Invoke-RestMethod -Uri http://127.0.0.1:9231/json_rpc -Method Post -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"0","method":"send_uptime_proof"}'
Daemon won’t start / crashes immediately
Check the logs:
docker logs sn01 --tail 100
Common issues:
- Port already in use. Another process is bound to 9230/9231/9232. Stop it or use different ports.
- Volume permission errors (Linux). Make sure the
sn01-datavolume is writable by the container’s user. - Image not found. Double-check that the image tag in your
docker runcommand matches the latest released version.
If the daemon keeps crashing with no clear error, post your docker logs sn01 --tail 200 output to the XEQMLabs community channels and someone will take a look.
Notes
- The registration command is one continuous line, so don’t introduce line breaks when copying.
- Registrations expire after 14 days if not submitted to the chain. Re-generate if you wait too long.
- Make sure your wallet has enough unlocked XEQM to cover the operator stake plus the transaction fee (a fraction of an XEQM is plenty).
- Pull the latest image periodically, since older versions may have known bugs that affect block production or earning rewards.
References
- XEQMLabs Docs, Service Node Operator Guide
- XEQMLabs, Developer Documentation
- XEQM Block Explorer, explorer.xeqmlabs.com
- XEQMLabs, GitHub
Related: XEQM tokenomics · Is XEQM a Monero fork? · How to buy XEQM