Skip to main content

Manual Node Setup

info

This guide is for advanced users. If you are not sure why you want a manual setup, use the automated SSV Stack setup.

Pre-requisites

1. Enable SSH

You need SSH access to your server:

How to SSH into a machine

See the EthStaker guide: https://docs.ethstaker.cc/ethstaker-knowledge-base/tutorials/connect-via-ssh

If you are using a cloud server, follow your provider's documentation. For example, AWS: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-ssh.html

2. Install Docker

Docker

Follow the official Docker documentation for the option that matches your server.


Docker usually requires sudo. If you want to avoid using it every time, you can grant the required permissions once: https://stackoverflow.com/questions/48957195/how-to-fix-docker-got-permission-denied-issue

3. Install Git

Git

On Debian or Ubuntu, run apt-get install git.

For other Operating Systems, see the official Git documentation.

4. Install Golang (optional)

Golang

If you want to build from source, install Go first.

For details, see the official Go installation instructions.

Generate Operator Keys (Encrypted)

The safest way to run your Operator node is with an encrypted key pair. This encrypts your Public Key (PK) and Secret Key (SK) with a password you choose.

Password file

Create a file named password with the password you want to use for your Secret Key:

echo YOUR_PASSWORD >> password

Key pair generation and encryption

Use the node Docker image to generate and encrypt the keys. Replace ./path_to_password with the real path to your password file:

docker run --name ssv-node-key-generation -v ./path_to_password:/password -it "ssvlabs/ssv-node:latest" /go/bin/ssvnode generate-operator-keys --password-file=password && docker cp ssv-node-key-generation:/encrypted_private_key.json ./encrypted_private_key.json && docker rm ssv-node-key-generation
Create backups

Back up encrypted_private_key.json and password on a separate device. If either file is lost, you will permanently lose access to your Operator.

Create Configuration File

Copy the config.yaml example below and replace the placeholders with your real values. The essential fields are editable.

warning

Make sure your ETH1Addr endpoint uses WebSocket, not HTTP.

global:
# Console output log level
LogLevel: info
# Debug logs file path
LogFilePath: ./data/debug.log
# Number of log files preserved, 500MB each (time duration depends on number of validators and other factors).
# Roughly equates to half a day.
# Increase if you want to preserve log files for longer. This would require more disk space
LogFileBackups: 10
db:
# Path to a persistent directory to store the node's database.
Path: ./data/db
ssv:
# The SSV network to join to
# Available options are mainnet and hoodi
Network: mainnet
ValidatorOptions:
# Block proposals are by default controlled by Beacon Node.
# Requires the connected Beacon node to be MEV-enabled.
# Please see https://docs.ssv.network/operators/operator-node/setup-sidecars/configuring-mev
eth2:
# HTTP URL of the Beacon node to connect to.
# If you want to use multiple endpoints you can divide them with ;
# e.g. http://example.url:5052;http://example.url:5053
BeaconNodeAddr: http://example.url:5052
# Both enable improved attestation accuracy from multiple Beacon nodes.
# Will have no effect with only 1 endpoint.
WithWeightedAttestationData: false
WithParallelSubmissions: false
eth1:
# WebSocket URL of the Eth1 node to connect to.
# If you want to use multiple endpoints you can divide them with ;
# e.g. ws://example.url:8546/ws;ws://example.url:8547/ws
ETH1Addr: ws://example.url:8546/ws
p2p:
# Optionally provide the external IP address of the node, if it cannot be automatically determined.
# HostAddress: 192.168.1.1
# Optionally override the default TCP & UDP ports of the node.
# TcpPort: 13001
# UdpPort: 12001
KeyStore:
PrivateKeyFile: ./encrypted_private_key.json
PasswordFile: ./password
# Enables Doppelganger Protection for validators, see https://github.com/ssvlabs/ssv/blob/v2.3.0/doppelganger/README.md
EnableDoppelgangerProtection: false
# This enables monitoring at the specified port, see https://docs.ssv.network/operators/operator-node/monitoring/
MetricsAPIPort: 15000
# This enables node health endpoint for troubleshooting, see https://docs.ssv.network/operators/operator-node/maintenance/troubleshooting
SSVAPIPort: 16000

Start the Node

Potential slashing

Do not run multiple SSV Node instances with the same Operator keys.

This does not improve resiliency and could lead to validator slashing.

Example docker-compose.yml:

services:
ssv:
image: ssvlabs/ssv-node:latest
ports:
- 13001:13001
- 12001:12001/udp
- 15000:15000
- 16000:16000
command:
make BUILD_PATH="/go/bin/ssvnode" start-node
volumes:
- ./config.yaml:/config/config.yaml
- ./data:/data
- ./password:/password
- ./encrypted_private_key.json:/encrypted_private_key.json
environment:
- CONFIG_PATH=/config/config.yaml
container_name: ssv_node
restart: unless-stopped
networks:
- ssv
networks:
ssv:
name: ssv
driver: bridge

Then run docker compose up from the same directory as docker-compose.yml.

  • This keeps the terminal attached so you can review logs during startup.
  • If everything looks good, use docker compose up -d to run in the background.

Peer-to-peer ports configuration and firewall

When configuring your firewall, open the same ports that you expose in the container command. By default these are 12001 UDP and 13001 TCP, plus 15000 TCP for metrics and 16000 TCP for the health endpoint.

If you change the default ports in config.yaml, update the container command as well. Changing only the host port mappings in Docker is not enough.

You can also set HostAddress in the config to the machine's public static IP address.

p2p:
HostAddress: 1.2.3.4
UdpPort: 12001
TcpPort: 13001

Database backups

SSV's database (db) is critical for slashing protection. If it is lost or corrupted and operation continues, it can lead to double-signing and severe penalties.

Implement a reliable backup and recovery strategy for your database. Operators are responsible for managing and protecting their own databases.

What's next?