> For the complete documentation index, see [llms.txt](https://xeqmlabs.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xeqmlabs.gitbook.io/docs/documentation/sn-operator-guide/service-node-startup-and-registration.md).

# Service Node Startup & Registration

This guide walks you through setting up a Service Node (SN) on the XEQM mainnet — from spinning up the daemon, to funding the operator stake, to generating and submitting your registration command.

If you can run a Docker container and copy/paste a few commands, you can run an SN. Plan on roughly 30–60 minutes for a clean first-time setup.

> **Note:** This guide uses the official XEQM Docker image, which is the simplest path for most operators. If you're running a self-built binary instead, the steps are similar but you'll skip the Docker bits — start the daemon directly with the same flags shown here, 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** that's 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 (DigitalOcean, Hetzner, Vultr, etc.).

***

### 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:

bash

```bash
mkdir -p ~/xeqm-sn01
cd ~/xeqm-sn01
```

Stop and remove any existing container (in case you're re-running):

bash

```bash
docker stop sn01 2>/dev/null
docker rm sn01 2>/dev/null
```

Pull the latest image:

bash

```bash
docker pull ghcr.io/xeqmlabs/xeqm-node:latest
```

Start the daemon. **Replace `YOUR_PUBLIC_IP`** with your server's actual public IP (find it with `curl ifconfig.me`):

bash

```bash
docker run -d \
  --name sn01 \
  --restart unless-stopped \
  --network host \
  -v "$(pwd)/data:/data" \
  ghcr.io/xeqmlabs/xeqm-node:latest \
  --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=seeds.xeqmlabs.com:9230 \
  --log-level=2 \
  --non-interactive
```

Watch the logs to confirm it's syncing:

bash

```bash
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:

powershell

```powershell
mkdir C:\xeqm-sn01
cd C:\xeqm-sn01
```

Stop and remove any existing container:

powershell

```powershell
docker stop sn01; docker rm sn01
```

Pull the latest image:

powershell

```powershell
docker pull ghcr.io/xeqmlabs/xeqm-node:v1.0.3
```

Start the daemon. **Replace `YOUR_PUBLIC_IP`** with your machine's actual public IP:

powershell

```powershell
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:latest `
  --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=seeds.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 — it's not reachable from the public internet. `--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:

powershell

```powershell
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

bash

```bash
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)

powershell

```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–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.

***

### 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: **Solo (full) node** or **Pool node**.

> **Operator cut on XEQM mainnet:** The operator cut is capped at **10% maximum**. The commands below use `operator_cut: "0"` as the default — you can raise this to anywhere between `"0"` and `"10"` depending on how much of a fee you want to keep on top of your own staked share. For solo nodes the cut doesn't matter (you're the only contributor either way), but 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**

bash

```bash
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:

bash

```bash
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)**

powershell

```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**

bash

```bash
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)**

powershell

```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:

* **`YOUR_WALLET_ADDRESS`** — your XEQM wallet address (starts with `XEQM`)

#### Expected output

A long single-line string that looks like:

```
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–2 minutes). You can confirm by checking your daemon:

#### Linux / Mac

bash

```bash
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)

powershell

```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
```

Get your SN's pubkey first with:

bash

```bash
# 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'
```

powershell

```powershell
# 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
```

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

bash

```bash
# 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'
```

powershell

```powershell
# 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:

```
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

bash

```bash
docker restart sn01
```

#### Update to the latest image version

bash

```bash
docker stop sn01
docker rm sn01
docker pull ghcr.io/xeqmlabs/xeqm-node:latest
# Then re-run the docker run command from Step 1
```

Your registration, keys, and chain data persist in the `data/` folder — 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 in particular, 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, bypassing port forwarding:

bash

```bash
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` (no commas, no decimals).

Also check your wallet address starts with `XEQM` and contains no whitespace.

#### My SN shows `active: false` or `payable: false`

* **`funded: false`** — the registration TX hasn't confirmed yet, or your operator stake hasn't fully committed. Wait a few blocks.
* **`active: true, payable: false`** — normal for the first 4 blocks after activation. The chain enforces a brief grace period before rewards begin.
* **`active: false`** with `decommission_count > 0` — 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 \~30 minutes once proofs resume.

#### "Last uptime proof" is more than 30 minutes ago

Your SN is at risk of decommission. Common causes:

* Daemon crashed or stopped (check `docker ps`)
* Port 9230 or 9232 is blocked by firewall
* Your `--service-node-public-ip` doesn't match your actual public IP

Fix the underlying issue and submit a fresh proof:

bash

```bash
# 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"}'
```

powershell

```powershell
# 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:

bash

```bash
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-data` volume is writable by the container's user.
* **Image not found** — double-check the image tag in your `docker run` command 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** — 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).
* Use the `:latest` image tag and pull periodically — older versions may have known bugs that affect block production or earning rewards.
