Practical Example: 3-Node Cluster¶
This example demonstrates a complete walkthrough for creating a 3-node cluster using Ethereum on ARM devices connected via a WireGuard VPN (using PiVPN as described in PiVPN and WireGuard: “Intra-VPN”).
Scenario:
3 Nodes: Node 0 (Leader), Node 1, Node 2.
Network: All nodes are connected via WireGuard VPN (see PiVPN and WireGuard: “Intra-VPN”).
Goal: Create a Distributed Validator with 1 faulty node tolerance (CFT).
Prerequisites:
Ensure all 3 nodes have the dvt-obol package installed and are reachable via their VPN IPs:
Node 0:
10.1.25.10Node 1:
10.1.25.11Node 2:
10.1.25.12
Cluster Diagram¶
+----------------+ +----------------+ +----------------+
| Node 0 | | Node 1 | | Node 2 |
| (Leader) | | (Operator) | | (Operator) |
| | | | | |
| VPN: 10.1.25.10| <------> | VPN: 10.1.25.11| <------> | VPN: 10.1.25.12|
+----------------+ +----------------+ +----------------+
Step 1: Generate ENRs on All Nodes¶
Run this command on Node 0, Node 1, and Node 2:
charon create enr --p2p-tcp-address=10.1.25.X:3610 --p2p-udp-address=10.1.25.X:3610
Replace `10.1.25.X` with the specific VPN IP of that node.
This command outputs an ENR string starting with enr:-.... Copy this ENR.
Send your ENR to the Cluster Leader (Node 0).
Step 2: Create Cluster Definition (Node 0 Only)¶
On Node 0, collect the ENRs from Node 1 and Node 2. Then run:
charon create dkg \
--name="ARM Cluster" \
--num-validators=1 \
--fee-recipient-addresses="0xYOUR_FEE_RECIPIENT_ADDRESS" \
--withdrawal-addresses="0xYOUR_WITHDRAWAL_ADDRESS" \
--operator-enrs="<ENR_NODE_0>,<ENR_NODE_1>,<ENR_NODE_2>"
Replace `<ENR_NODE_X>` with the actual ENR strings, separated by commas (no spaces).
This creates the cluster-definition.json file.
Transfer this file to Node 1 and Node 2 (e.g., using scp via the VPN IPs).
Step 3: Run DKG Ceremony (All Nodes)¶
Once every node has the cluster-definition.json file, run this command simultaneously on all 3 nodes:
charon dkg --definition-file=/path/to/cluster-definition.json
Wait for the process to complete. It will generate:
- cluster-lock.json
- validator_keys/
- deposit-data.json
Step 4: Configure Charon on Each Node¶
On each node, edit the configuration file /etc/ethereum/dvt/charon.conf to ensure it listens on the VPN interface or all interfaces:
Node 0 (10.1.25.10):
ARGS="...
--p2p-tcp-addresses 10.1.25.X:3610 \
--p2p-udp-addresses 10.1.25.X:3610 \
--p2p-relays=\"\" \
--monitoring-address 0.0.0.0:3620 \
..."
Note: We bind P2P addresses to the VPN IP (10.1.25.X) to ensure traffic flows over the VPN. We also set `–p2p-relays=””` to disable public relays, ensuring a fully private cluster.
Step 5: Start Services
On all nodes:
sudo systemctl enable --now charon.service
sudo systemctl enable --now lighthouse-validator-obol.service
(Replace `lighthouse` with your consensus client if different)
Step 6: Centralized Monitoring¶
To monitor the cluster from a single dashboard (e.g., on Node 0), add all nodes to your prometheus.yml:
- job_name: "charon_cluster"
metrics_path: "/metrics"
static_configs:
- targets: ["10.1.25.10:3620"]
labels:
dvt_provider: "obol"
node_id: "0"
- targets: ["10.1.25.11:3620"]
labels:
dvt_provider: "obol"
node_id: "1"
- targets: ["10.1.25.12:3620"]
labels:
dvt_provider: "obol"
node_id: "2"