Guide to Staking on Ethereum (Ubuntu/Prysm)

A Note on the Merge

  • Create a JWT file (see Step 7).
  • Update your Execution Client (Eth1) client software version and service config based on the settings in this guide (see Step 8). If you don’t have an Execution Client you will need to install and sync one.
  • Update your Consensus Client (Eth2) client software version and service config based on the settings in this guide. No need to reimport your validators (see Step 11 and Step 12).

Warnings

Disclaimer

Acknowledgements

Support

  • The EthStaker community on Reddit or Discord. Knowledgeable and friendly community passionate about staking on Ethereum.
  • The Prysm Consensus Client team Discord.
  • The Besu / Erigon / Geth / Nethermind Execution Client team Discord.

Prerequisites

  • Ubuntu server v20.04 (LTS) amd64 installed and running on a local computer or in the cloud. A locally running computer is encouraged for greater decentralization.
  • MetaMask crypto wallet web browser extension installed and configured on a computer with a desktop (Mac, Windows, Linux, etc.) and a web browser (Brave, Safari, FireFox, etc.).

Testnet to Mainnet

Requirements

  • A relatively modern multi-core CPU.
  • 8GB RAM (16GB is better, and required in some cases).
  • An SSD (NVMe is better) of at least 1TB (2TB is strongly recommended).
  • A stable internet connection with sufficient download speed and monthly data allowance.

Activation Queue

Overview

  • Generate the staking Deposit Data and Validator Keystore(s)
  • Prepare the Ubuntu Server (updates, firewall, security, etc.)
  • Set up an Execution Client node and sync it with the Ethereum mainnet
  • Set up the Prysm Consensus Client and sync with other Beacon Nodes
  • Deposit 32 ETH to activate the staking Validator(s)

Step 1 — Generate Staking Data

Download the Deposit Tool (Staking Deposit CLI)

$ cd ~
$ curl -LO https://github.com/ethereum/staking-deposit-cli/releases/download/v2.3.0/staking_deposit-cli-76ed782-linux-amd64.tar.gz
$ tar xvf staking_deposit-cli-76ed782-linux-amd64.tar.gz
$ mv staking_deposit-cli-76ed782-linux-amd64 staking-deposit-cli
$ rm staking_deposit-cli-76ed782-linux-amd64.tar.gz # <-- Clean up
$ cd staking-deposit-cli

Prepare to Run the Deposit Tool (Staking Deposit CLI)

Run the Deposit Tool (Staking Deposit CLI)

$ sudo ./deposit new-mnemonic --num_validators 2 --chain mainnet
deposit.exe new-mnemonic --num_validators 2 --chain mainnet
  • The deposit_data-[timestamp].json file contains the public key(s) for the validator(s) and information about the staking deposit. This file will be used to complete the ETH staking deposit process later in this guide.
  • The keystore-[..].json files contain the encrypted validator signing key. There is one keystore file per validator that you are funding. These will be imported into the Consensus Client for use during validation operations.
  • You will copy the files over to your Ubuntu server (if not already there) later in this guide.
  • If you lose or accidentally delete the files it is possible to regenerate them using the Staking Deposit CLI tool and your mnemonic via the existing-mnemonic command. More information here.

Final Steps

Step 2 — Create the Server User

# adduser <yourusername>
# usermod -aG sudo <yourusername>
# rsync --archive --chown=<yourusername>:<yourusername> ~/.ssh /home/<yourusername>

Step 3 — Update the Server

$ sudo apt -y update && sudo apt -y upgrade
$ sudo apt dist-upgrade && sudo apt autoremove
$ sudo reboot

Step 4 — Secure the Server

Modify the Default SSH Port

$ sudo ss -tulpn | grep ':YourSSHPortNumber'
$ sudo ss -tulpn | grep ':6673'
$ sudo nano /etc/ssh/sshd_config
Port <YourSSHPortNumber>
$ sudo systemctl restart ssh

Configure the Firewall

$ sudo apt install ufw
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
$ sudo ufw allow YourSSHPortNumber/tcp
$ sudo ufw allow 6673/tcp
$ sudo ufw deny 22/tcp
$ sudo ufw allow 30303
$ sudo ufw allow 13000/tcp
$ sudo ufw allow 12000/udp

Enable the Firewall

$ sudo ufw enable
$ sudo ufw status numbered

Step 5 — Create a Swap Space

$ free -h
RAM     Swap Size
8GB 3GB
12GB 3GB
16GB 4GB
24GB 5GB
32GB 6GB
64GB 8GB
128GB 11GB
$ df -h
$ sudo fallocate -l 3G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ free -h
$ sudo cp /etc/fstab /etc/fstab.bak
$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
$ sudo sysctl vm.swappiness=10
$ sudo sysctl vm.vfs_cache_pressure=50
$ sudo nano /etc/sysctl.conf
vm.swappiness=10
vm.vfs_cache_pressure=50

Step 6 — Configure Timekeeping

$ timedatectl
$ sudo timedatectl set-ntp on

Step 7 — Generate Client Authentication Secret

$ sudo mkdir -p /var/lib/jwtsecret
$ openssl rand -hex 32 | sudo tee /var/lib/jwtsecret/jwt.hex > /dev/null
$ sudo nano /var/lib/jwtsecret/jwt.hex

Step 8 — Configure the Execution Client

Install the Execution Client — Besu

$ cd ~
$ curl -LO https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/22.7.2/besu-22.7.2.tar.gz
$ tar xvf besu-22.7.2.tar.gz
$ sudo cp -a besu-22.7.2 /usr/local/bin/besu
$ rm besu-22.7.2.tar.gz
$ rm -r besu-22.7.2
$ sudo apt -y install default-jre
$ sudo apt install -y libjemalloc-dev
$ sudo useradd --no-create-home --shell /bin/false besu
$ sudo mkdir -p /var/lib/besu
$ sudo chown -R besu:besu /var/lib/besu
$ sudo nano /etc/systemd/system/besu.service
[Unit]
Description=Besu Execution Client (Mainnet)
Wants=network-online.target
After=network-online.target
[Service]
User=besu
Group=besu
Type=simple
Restart=always
RestartSec=5
Environment="JAVA_OPTS=-Xmx4g"
ExecStart=/usr/local/bin/besu/bin/besu \
--network=mainnet \
--sync-mode=X_SNAP \
--data-path=/var/lib/besu \
--data-storage-format=BONSAI \
--engine-jwt-secret=/var/lib/jwtsecret/jwt.hex
[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl start besu
$ sudo systemctl status besu
$ sudo journalctl -fu besu
$ sudo systemctl enable besu

Install the Execution Client — Erigon

$ cd ~
$ curl -LO https://go.dev/dl/go1.19.linux-amd64.tar.gz
$ sudo rm -rf /usr/local/go
$ sudo tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz
$ echo 'PATH="$PATH:/usr/local/go/bin"' >> $HOME/.profile
$ source $HOME/.profile
$ rm go1.19.linux-amd64.tar.gz
$ sudo apt-get install -y build-essential
$ cd ~
$ curl -LO https://github.com/ledgerwatch/erigon/archive/refs/tags/v2022.09.02.tar.gz
$ tar xvf v2022.09.02.tar.gz
$ cd erigon-2022.09.02
$ make erigon
$ cd ~
$ sudo cp -a erigon-2022.09.02 /usr/local/bin/erigon
$ rm v2022.09.02.tar.gz
$ rm -r erigon-2022.09.02
$ sudo useradd --no-create-home --shell /bin/false erigon
$ sudo mkdir -p /var/lib/erigon
$ sudo chown -R erigon:erigon /var/lib/erigon
$ sudo nano /etc/systemd/system/erigon.service
[Unit]
Description=Erigon Execution Client (Mainnet)
After=network.target
Wants=network.target
[Service]
User=erigon
Group=erigon
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/erigon/build/bin/erigon \
--chain=mainnet \
--datadir=/var/lib/erigon \
--authrpc.jwtsecret=/var/lib/jwtsecret/jwt.hex \
--private.api.addr= \
--prune.r.before=11052984 \
--prune htc
[Install]
WantedBy=default.target
$ /usr/local/bin/erigon/build/bin/./erigon --help
$ sudo systemctl daemon-reload
$ sudo systemctl start erigon
$ sudo systemctl status erigon
$ sudo journalctl -fu erigon
$ sudo systemctl enable erigon
$ cd ~
$ curl -LO https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.25-69568c55.tar.gz
$ tar xvf geth-linux-amd64-1.10.25-69568c55.tar.gz
$ cd geth-linux-amd64-1.10.25-69568c55
$ sudo cp geth /usr/local/bin
$ cd ~
$ rm geth-linux-amd64-1.10.25-69568c55.tar.gz
$ rm -r geth-linux-amd64-1.10.25-69568c55
$ sudo useradd --no-create-home --shell /bin/false geth
$ sudo mkdir -p /var/lib/geth
$ sudo chown -R geth:geth /var/lib/geth
$ sudo nano /etc/systemd/system/geth.service
[Unit]
Description=Geth Execution Client (Mainnet)
After=network.target
Wants=network.target
[Service]
User=geth
Group=geth
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/geth \
--mainnet \
--datadir /var/lib/geth \
--authrpc.jwtsecret /var/lib/jwtsecret/jwt.hex
[Install]
WantedBy=default.target
$ sudo systemctl daemon-reload
$ sudo systemctl start geth
$ sudo systemctl status geth
$ sudo journalctl -fu geth
$ sudo systemctl enable geth

Install the Execution Client — Nethermind

$ cd ~
$ curl -LO https://github.com/NethermindEth/nethermind/releases/download/1.14.2/nethermind-linux-amd64-1.14.2-08354f9-20220915.zip
$ sudo apt-get install -y unzip
$ unzip nethermind-linux-amd64-1.14.2-08354f9-20220915.zip -d nethermind
$ sudo cp -a nethermind /usr/local/bin/nethermind
$ rm nethermind-linux-amd64-1.14.2-08354f9-20220915.zip
$ rm -r nethermind
$ sudo apt-get update
$ sudo apt-get install libsnappy-dev libc6-dev libc6 unzip -y
$ sudo useradd --no-create-home --shell /bin/false nethermind
$ sudo mkdir -p /var/lib/nethermind
$ sudo chown -R nethermind:nethermind /var/lib/nethermind
$ sudo nano /etc/systemd/system/nethermind.service
[Unit]
Description=Nethermind Execution Client (Mainnet)
After=network.target
Wants=network.target
[Service]
User=nethermind
Group=nethermind
Type=simple
Restart=always
RestartSec=5
WorkingDirectory=/var/lib/nethermind
Environment="DOTNET_BUNDLE_EXTRACT_BASE_DIR=/var/lib/nethermind"
ExecStart=/usr/local/bin/nethermind/Nethermind.Runner \
--config mainnet \
--datadir /var/lib/nethermind \
--Sync.SnapSync true \
--Sync.AncientBodiesBarrier 11052984 \
--Sync.AncientReceiptsBarrier 11052984 \
--JsonRpc.JwtSecretFile /var/lib/jwtsecret/jwt.hex
[Install]
WantedBy=default.target
$ sudo systemctl daemon-reload
$ sudo systemctl start nethermind
$ sudo systemctl status nethermind
$ sudo journalctl -fu nethermind
$ sudo systemctl enable nethermind

Step 9 — Install the Prysm Consensus Client

$ cd ~
$ curl -LO https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64
$ curl -LO https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/validator-v3.1.1-linux-amd64
$ mv beacon-chain-v3.1.1-linux-amd64 beacon-chain
$ mv validator-v3.1.1-linux-amd64 validator
$ chmod +x beacon-chain
$ chmod +x validator
$ sudo cp beacon-chain /usr/local/bin
$ sudo cp validator /usr/local/bin
$ rm beacon-chain && rm validator

Step 10 — Import the Validator Keys

Copy the Validator Keystore Files to the Server

$ sudo mkdir -p $HOME/staking-deposit-cli/validator_keys
$ sudo chown -R <yourusername>:<yourusername> $HOME/staking-deposit-cli/validator_keys

Import the Validator Keystore Files into Prysm

$ sudo mkdir -p /var/lib/prysm/validator
$ sudo /usr/local/bin/validator accounts import --keys-dir=$HOME/staking-deposit-cli/validator_keys --wallet-dir=/var/lib/prysm/validator --mainnet

Create a Wallet Password File

$ sudo nano /var/lib/prysm/validator/password.txt

Step 11 — Configure the Beacon Node Service

Set up the Account

$ sudo useradd --no-create-home --shell /bin/false prysmbeacon

Set up the Directories and the Permissions

$ sudo mkdir -p /var/lib/prysm/beacon
$ sudo chown -R prysmbeacon:prysmbeacon /var/lib/prysm/beacon

Create and Configure the Service

$ sudo nano /etc/systemd/system/prysmbeacon.service
[Unit]
Description=Prysm Consensus Client BN (Mainnet)
Wants=network-online.target
After=network-online.target
[Service]
User=prysmbeacon
Group=prysmbeacon
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/beacon-chain \
--mainnet \
--datadir=/var/lib/prysm/beacon \
--execution-endpoint=http://127.0.0.1:8551 \
--jwt-secret=/var/lib/jwtsecret/jwt.hex \
--suggested-fee-recipient=FeeRecipientAddress \
--checkpoint-sync-url=CheckpointSyncURL \
--genesis-beacon-api-url=CheckpointSyncURL \
--accept-terms-of-use
[Install]
WantedBy=multi-user.target
  • Via the dashboard, click on the Manage Key button. Copy the Mainnet endpoint https URL and use it to replace both occurrences of CheckpointSyncURL in the service configuration above.
  • E.g. --checkpoint-sync-url=https://29geMekrvT0kz5EKbfadEesRnje:82768b7f045268191a8e8810de0a55c7@eth2-beacon-mainnet.infura.io \
$ sudo systemctl daemon-reload
$ sudo systemctl start prysmbeacon
$ sudo systemctl status prysmbeacon
$ sudo journalctl -fu prysmbeacon
$ sudo systemctl enable prysmbeacon

Step 12 — Configure the Validator Service

Set up the Validator Node Account and Directory

$ sudo useradd --no-create-home --shell /bin/false prysmvalidator
$ sudo chown -R prysmvalidator:prysmvalidator /var/lib/prysm/validator

Create and Configure the Service

$ sudo nano /etc/systemd/system/prysmvalidator.service
[Unit]
Description=Prysm Consensus Client VC (Mainnet)
Wants=network-online.target
After=network-online.target
[Service]
User=prysmvalidator
Group=prysmvalidator
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/validator \
--datadir=/var/lib/prysm/validator \
--wallet-dir=/var/lib/prysm/validator \
--wallet-password-file=/var/lib/prysm/validator/password.txt \
--suggested-fee-recipient=FeeRecipientAddress \
--graffiti="<yourgraffiti>" \
--accept-terms-of-use
[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl start prysmvalidator
$ sudo systemctl status prysmvalidator
$ sudo journalctl -fu prysmvalidator
$ sudo systemctl enable prysmvalidator

Step 13 — Fund the Validator Keys

Check the Status of Your Validator Deposits

  1. Copy your MetaMask (or other) wallet address used to make the deposit.
  2. Go here: https://beaconcha.in/
  3. Search for your key(s) using your wallet address.

Final Remarks and Recommended Next Steps

Next steps

  • Reboot your machine and make sure all services come back up
  • Understand how to update the client and server software
  • Use htop, df -h, or ncdu / to monitor resources on the local machine
  • Review Appendix H — Manage Systemd Journal Logs to manage log size
  • Get familiar with beaconcha.in so you can monitor your validators
  • Use the beaconcha.in mobile app to monitor your validators
  • Check my testnet guides for details on setting up Grafana monitoring
  • Join the Ethstaker and Prysm Discord for important notifications
  • Share feedback on Discord (Somer#0753), Twitter, or Reddit
  • Help others with their setup on the Ethstaker Discord
  • Share this guide with your friends!
  • Tips appreciated: somer.eth or Gitcoin Grants

Further Reading

Appendix A — Updating Besu

$ cd ~
$ curl -LO https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/22.7.2/besu-22.7.2.tar.gz
$ sudo systemctl stop besu
$ tar xvf besu-22.7.2.tar.gz
$ sudo rm -r /usr/local/bin/besu # <-- Remove the old files
$ sudo cp -a besu-22.7.2 /usr/local/bin/besu
$ sudo systemctl start besu
$ sudo systemctl status besu # <-- Check for errors
$ sudo journalctl -fu besu # <-- Monitor
$ sudo journalctl -fu prysmbeacon # <-- Monitor
$ cd ~
$ rm besu-22.7.2.tar.gz
$ rm -r besu-22.7.2

Appendix B — Updating Erigon

$ cd ~
$ curl -LO https://github.com/ledgerwatch/erigon/archive/refs/tags/v2022.09.02.tar.gz
$ tar xvf v2022.09.02.tar.gz
$ cd erigon-2022.09.02
$ make erigon
$ sudo systemctl stop erigon
$ cd ~
$ sudo rm -r /usr/local/bin/erigon # <-- Remove the old files
$ sudo cp -a erigon-2022.09.02 /usr/local/bin/erigon
$ sudo systemctl start erigon
$ sudo systemctl status erigon # <-- Check for errors
$ sudo journalctl -fu erigon <-- Monitor
$ sudo journalctl -fu prysmbeacon # <-- Monitor
$ rm v2022.09.02.tar.gz
$ rm -r erigon-2022.09.02

Appendix C — Updating Geth

$ cd ~
$ curl -LO https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.25-69568c55.tar.gz
$ sudo systemctl stop geth
$ tar xvf geth-linux-amd64-1.10.25-69568c55.tar.gz
$ cd geth-linux-amd64-1.10.25-69568c55
$ sudo cp geth /usr/local/bin
$ sudo systemctl start geth
$ sudo systemctl status geth # <-- Check for errors
$ sudo journalctl -fu geth # <-- Monitor
$ sudo journalctl -fu prysmbeacon # <-- Monitor
$ cd ~
$ rm geth-linux-amd64-1.10.25-69568c55.tar.gz
$ rm -r geth-linux-amd64-1.10.25-69568c55
$ sudo systemctl stop geth
$ sudo apt update && sudo apt upgrade
$ sudo systemctl start geth
$ sudo systemctl status geth # <-- Check for errors
$ sudo journalctl -fu geth # <-- Monitor
$ sudo journalctl -fu prysmbeacon # <-- Monitor

Appendix D — Updating Nethermind

$ cd ~
$ curl -LO https://github.com/NethermindEth/nethermind/releases/download/1.14.2/nethermind-linux-amd64-1.14.2-08354f9-20220915.zip
$ sudo systemctl stop nethermind
$ unzip nethermind-linux-amd64-1.14.2-08354f9-20220915.zip -d nethermind
$ sudo rm -r /usr/local/bin/nethermind # <-- Remove the old files
$ sudo cp -a nethermind /usr/local/bin/nethermind
$ sudo systemctl start nethermind
$ sudo systemctl status nethermind # <-- Check for errors
$ sudo journalctl -fu nethermind # <-- Monitor
$ sudo journalctl -fu prysmbeacon # <-- Monitor
$ cd ~
$ rm nethermind-linux-amd64-1.14.2-08354f9-20220915.zip
$ rm -r nethermind

Appendix E — Updating Prysm

$ cd ~
$ curl -LO https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/beacon-chain-v3.1.1-linux-amd64
$ curl -LO https://github.com/prysmaticlabs/prysm/releases/download/v3.1.1/validator-v3.1.1-linux-amd64
$ sudo systemctl stop prysmvalidator
$ sudo systemctl stop prysmbeacon
$ mv beacon-chain-v3.1.1-linux-amd64 beacon-chain
$ mv validator-v3.1.1-linux-amd64 validator
$ chmod +x beacon-chain
$ chmod +x validator
$ sudo cp beacon-chain /usr/local/bin
$ sudo cp validator /usr/local/bin
$ sudo systemctl start prysmbeacon
$ sudo systemctl status prysmbeacon # <-- Check for errors
$ sudo journalctl -fu prysmbeacon # <-- Monitor
$ sudo systemctl start prysmvalidator
$ sudo systemctl status prysmvalidator # <-- Check for errors
$ sudo journalctl -fu prysmvalidator # <-- Monitor
$ rm beacon-chain && rm validator

Appendix F — Adding Validators

Generate Deposit Data

$ sudo ./deposit existing-mnemonic --validator_start_index <ValidatorStartIndex> --num_validators <NumberOfValidators> --chain mainnet
deposit.exe existing-mnemonic --validator_start_index <ValidatorStartIndex> --num_validators <NumberOfValidators> --chain mainnet
  • The newer deposit_data-[timestamp].json file contains the public key(s) for the newly added validator(s) and information about the staking deposit. This file will be used to complete the ETH staking deposit process later in this guide.
  • The newly created keystore-[..].json file(s) contain the encrypted validator signing key. There is one keystore file per additional validator that you are funding. These will be imported into the Consensus Client validator wallet for use during validation operations.
  • You will copy the files over to your Ubuntu server (if not already there) later in this step.
  • If you lose or accidentally delete the files it is possible to regenerate them using the Staking Deposit Tool and your mnemonic via the existing-mnemonic command. More information here.

Copy the Validator Keystore Files to the Server

$ sudo systemctl stop prysmvalidator
$ sudo /usr/local/bin/validator accounts import --keys-dir=$HOME/staking-deposit-cli/validator_keys --wallet-dir=/var/lib/prysm/validator --mainnet
$ sudo /usr/local/bin/validator accounts list --wallet-dir=/var/lib/prysm/validator --mainnet
$ sudo chown -R prysmvalidator:prysmvalidator /var/lib/prysm/validator
$ sudo systemctl start prysmvalidator
$ sudo systemctl status prysmvalidator # <-- Check for errors
$ sudo journalctl -fu prysmvalidator # <-- Monitor
msg="Validating for public key" prefix=validator publicKey=0x87aef47324f0
msg="Validating for public key" prefix=validator publicKey=0x87432f581d95
msg="Validating for public key" prefix=validator publicKey=0x8a0e3be52467

Appendix G — Exiting Validators

$ sudo /usr/local/bin/validator accounts voluntary-exit --wallet-dir=/var/lib/prysm/validator

Appendix H — Manage Systemd Journal Logs

$ sudo journalctl --disk-usage
$ sudo journalctl --flush --rotate 
$ sudo journalctl --vacuum-time=3days
$ sudo journalctl --verify

Automatically Limit Log Size

$ sudo nano /etc/systemd/journald.conf
$ sudo systemctl restart systemd-journald

Appendix I — Expanding the Logical Volume

$ sudo lvdisplay # <-- Check your logical volume size
$ sudo lvm
> lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
> lvextend -l +100%FREE -r /dev/ubuntu-vg/ubuntu-lv
> exit
$ sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
$ df -h # <-- Check results

Full Disclaimer

--

--

Passionate about Ethereum and decentralized technology.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Somer Esat

Somer Esat

Passionate about Ethereum and decentralized technology.