Cardano Relay Node
This example deploys a Cardano mainnet relay node on Compute Portal — bootstrapped via a Mithril snapshot so the chain DB restores in hours rather than days. The node uses a persistent volume for chain data, a ConfigMap for all mainnet config files, and (optionally) a public TCP endpoint so an external block producer can reach it.
| Resource | Role |
|---|---|
cardano-relay | cardano-node 11.0.1, Cardano mainnet P2P relay |
cardano-relay-config | ConfigMap holding all 6 mainnet config files |
/data volume | 500 GB persistent chain DB (~221 GB today, grows ~2–5 GB/month) |
Prerequisites
Server provisioning required
This guide uses a dedicated compute resource (server-type). If you have not yet purchased one, complete your order at computeportal.io/start. Server-type resources take up to 24 business hours to provision — you will receive an email when yours is ready. Return to this page after that email arrives.
New to the two-node model? Read the Cardano Stake Pool Setup guide first.
- cpctl installed and authenticated (
cpctl login --token <token>) - A CP account with a dedicated compute resource assigned — the relay needs 6 CPU cores and 20 GiB RAM. Contact support at computeportal.io if not assigned.
- Verify quota before starting:
bash
cpctl quota┌──────────────────┬──────────┬──────────┐
│ RESOURCE │ USED │ LIMIT │
├──────────────────┼──────────┼──────────┤
│ CPU │ 0.00 │ 8.00 │
│ Memory │ 0.0 GiB │ 64.0 GiB │
│ Services │ 0 │ 20 │
└──────────────────┴──────────┴──────────┘You need at least 6 CPU cores and 20 GiB RAM available.
Step 1 — Prepare config files
Download the six standard Cardano mainnet config files into a data/ directory:
data/
├── config.json ← main node configuration
├── topology.json ← P2P peer topology
├── byron-genesis.json
├── shelley-genesis.json
├── alonzo-genesis.json
└── conway-genesis.jsonThe latest versions are available from the IOHK Cardano configurations page.
topology.json
Use localRoots only with useLedgerAfterSlot: -1 to keep peer discovery IPv4-only and stable. Ledger-based peer discovery triggers DNS lookups for AAAA records, which fail silently if your compute node has no IPv6 routing.
json
{
"bootstrapPeers": [
{ "address": "backbone.cardano.iog.io", "port": 3001 },
{ "address": "backbone.mainnet.cardanofoundation.org", "port": 3001 }
],
"localRoots": [
{
"accessPoints": [
{ "address": "relays-new.cardano-mainnet.iohk.io", "port": 3001 },
{ "address": "relay1.mainnet.cardanofoundation.org", "port": 3001 },
{ "address": "relay2.mainnet.cardanofoundation.org", "port": 3001 },
{ "address": "north-america.relays-new.cardano-mainnet.iohk.io", "port": 3001 },
{ "address": "europe.relays-new.cardano-mainnet.iohk.io", "port": 3001 },
{ "address": "asia-pacific.relays-new.cardano-mainnet.iohk.io", "port": 3001 },
{ "address": "relay.emurgo.io", "port": 3001 }
],
"advertise": false,
"trustable": true,
"valency": 6
}
],
"publicRoots": [],
"useLedgerAfterSlot": -1
}Step 2 — Upload config files as a ConfigMap
Upload all six files in one command. The key for each file is its filename; all keys are mounted into the container at /data/config/.
bash
cpctl configmap create cardano-relay-config \
--from-file data/config.json \
--from-file data/topology.json \
--from-file data/byron-genesis.json \
--from-file data/shelley-genesis.json \
--from-file data/alonzo-genesis.json \
--from-file data/conway-genesis.jsonUploading config.json (12 KB)...
Uploading topology.json (1 KB)...
Uploading byron-genesis.json (4 KB)...
Uploading shelley-genesis.json (42 KB)...
Uploading alonzo-genesis.json (1 KB)...
Uploading conway-genesis.json (140 KB)...
✓ Config map "cardano-relay-config" created
name cardano-relay-config
keys config.json, topology.json, byron-genesis.json, shelley-genesis.json, alonzo-genesis.json, conway-genesis.jsonStep 3 — Deploy with Mithril bootstrap
A single cpctl deploy command starts the node. An init container runs Mithril first, downloads and verifies a recent mainnet snapshot (~221 GB) into the persistent volume, then exits — cardano-node starts only after the snapshot is in place.
bash
cpctl deploy \
--image ghcr.io/intersectmbo/cardano-node:11.0.1 \
--name cardano-relay \
--port 3001 \
--no-probe \
--init-image ghcr.io/intersectmbo/mithril-client:latest \
--init-command 'if [ -f /data/db/protocolMagicId ]; then
echo "DB already bootstrapped, skipping Mithril download"; exit 0
fi
echo "Starting Mithril snapshot download..."
/app/bin/mithril-client cardano-db download latest \
--download-dir /data --include-ancillary' \
--init-env CARDANO_NETWORK=mainnet \
--init-env AGGREGATOR_ENDPOINT=https://aggregator.release-mainnet.api.mithril.network/aggregator \
--init-env GENESIS_VERIFICATION_KEY=5b3139312c36362c3134302c3138352c3133382c31312c3233372c3230372c3235302c3134342c32372c322c3138382c33302c31322c38312c3135352c3230342c31302c3137392c37352c32332c3133382c3139362c3231372c352c31342c32302c35372c37392c33392c3137365d \
--init-env ANCILLARY_VERIFICATION_KEY=5b32332c37312c39362c3133332c34372c3235332c3232362c3133362c3233352c35372c3136342c3130362c3138362c322c32312c32392c3132302c3136332c38392c3132312c3137372c3133382c3230382c3133382c3231342c39392c35382c32322c302c35382c332c36395d \
--mount-configmap cardano-relay-config:/data/config \
-yFlag notes:
| Flag | Why |
|---|---|
--no-probe | cardano-node doesn't serve HTTP — skip the TCP readiness probe |
--init-image | Mithril client runs before cardano-node starts |
--init-command | Idempotent: skips download if the DB is already present (restart-safe) |
--init-env | Mainnet Mithril aggregator URL and public verification keys |
--mount-configmap | All 6 config files mounted at /data/config/ inside the container |
Bootstrap timeline
| Phase | What happens | Approx. time |
|---|---|---|
| Mithril download | ~221 GB snapshot streamed to /data/db | 20–40 min |
| Mithril verification | All immutable chunks verified via SPO threshold signatures | 2–8 hours |
| cardano-node startup | Ledger state replayed from snapshot | 15–30 min |
| Block sync | Remaining blocks synced from peers | 1–3 hours |
Step 4 — Scale resources and attach storage
bash
# 6 CPU cores and 20 GiB RAM — tuned for the RTS flags below
cpctl scale cardano-relay --cpu 6 --ram 20g
# 500 GB persistent volume — gives ~2 years of headroom at current chain growth
cpctl volume add cardano-relay /data --size 500Set runtime environment variables (Cardano-specific paths and GHC RTS tuning):
bash
cpctl env set cardano-relay \
CARDANO_PORT=3001 \
CARDANO_DB=/data/db \
CARDANO_CONFIG=/data/config/config.json \
CARDANO_TOPOLOGY=/data/config/topology.json \
CARDANO_NODE_SOCKET_PATH=/ipc/node.socket \
NETWORK=mainnet \
"CARDANO_RTS_FLAGS=+RTS -N4 -A16m -qg1 -qb1 -RTS"-N4 runs 4 GHC threads — matched to the 6-core allocation (4 worker threads
- OS and I/O overhead). Adjust
CARDANO_RTS_FLAGSviacpctl env setif your CPU allocation differs.
Step 5 — Monitor sync progress
Stream logs while Mithril downloads and cardano-node syncs:
bash
cpctl logs cardano-relay --followOnce cardano-node is running, check sync progress:
bash
cpctl exec cardano-relay -- cardano-cli query tip --mainnetjson
{
"block": 13909082,
"epoch": 654,
"era": "Conway",
"hash": "7f3b...",
"slot": 137842910,
"slotInEpoch": 125150,
"slotsToEpochEnd": 307250,
"syncProgress": "1.00"
}syncProgress: "1.00" means the relay is fully synced and participating in the Cardano P2P network.
Step 6 — Expose to an external block producer (optional)
If your block producer runs outside Compute Portal (bare metal, VPS, or home machine), it needs to reach the relay over raw TCP on port 3001. Standard HTTPS ingress does not work for Cardano's binary P2P protocol — use cpctl expose to open a direct TCP path.
bash
cpctl expose cardano-relay --port 3001✓ Exposed cardano-relay at tcp.cpctl.app:3001
Point your client at: tcp.cpctl.app:3001Confirm the endpoint:
bash
cpctl expose list cardano-relayHOST PORT NODE PORT CREATED AT
tcp.cpctl.app 3001 30283 2026-09-05T08:00:00ZPoint your block producer's topology.json at tcp.cpctl.app:3001:
json
{
"bootstrapPeers": [],
"localRoots": [
{
"accessPoints": [
{ "address": "tcp.cpctl.app", "port": 3001 }
],
"advertise": false,
"valency": 1,
"warmValency": 1
}
],
"publicRoots": [],
"useLedgerAfterSlot": -1
}Traffic path: block producer → tcp.cpctl.app:3001 → nginx stream → NodePort → cardano-relay pod
To remove the public endpoint:
bash
cpctl expose delete cardano-relay 3001Updating config files
To push a topology or config change after initial deployment:
bash
cpctl configmap delete cardano-relay-config
cpctl configmap create cardano-relay-config \
--from-file data/config.json \
--from-file data/topology.json \
--from-file data/byron-genesis.json \
--from-file data/shelley-genesis.json \
--from-file data/alonzo-genesis.json \
--from-file data/conway-genesis.json
cpctl machine restart cardano-relayThe restart picks up the new ConfigMap contents immediately — no redeployment needed.
Useful commands
bash
# Sync progress
cpctl exec cardano-relay -- cardano-cli query tip --mainnet
# Live logs
cpctl logs cardano-relay --follow
# Shell access
cpctl exec cardano-relay -- bash
# Resource usage
cpctl status cardano-relay
# Storage
cpctl volume list cardano-relay
# Metrics
cpctl metrics cardano-relayResource sizing reference
| Resource | Value | Notes |
|---|---|---|
| CPU | 6 cores | -N4 RTS threads + OS overhead |
| RAM | 20 GiB | cardano-node ledger state is memory-resident |
| Storage | 500 GB | ~221 GB today; grows ~2–5 GB/month |
| Port | 3001 | Cardano P2P (binary protocol, not HTTP) |
