Skip to main content

Table of Contents

Step 0 Purchase Equipment and Assemble NUC

First, you’ll need hardware to run the validator software on. Requirements can be found here as of 1/27/25, suggested hardware are as follows:
  • Asus NUC 14+ Pro
  • 32GB RAM (64GB preferred)
  • NVMe M.2 4TB
  • Ethernet connection with 100 Mbps symmetrical (300 Mbps preferred)
  • USB drive (used to install Linux) 4GB+
  • HDMI (or USB) cord and connectable monitor for initial set up
  • USB keyboard and mouse (mouse is only required if using Ubuntu desktop to generate new validator keys)
While not a hard requirement, it’s also recommended to purchase a 1000+VA sine wave UPS to act as a battery backup in the case of a power failure. Note that in order for this to be effective, you should plug the validator and internet infrastructure into the UPS. The goal is to guard against both unclean shutdowns as well as downtime. Once equipment has been obtained, simply install the RAM and SSD in the NUC. Because some of the below processes require using an air-gapped machine, do not connect the device to the internet. Note: You will also need a small screwdriver to handle installation if assembling yourself.

Step 1 Generate Staking Data

Only if setting up a new validator: If setting up a new validator (and haven’t already generated deposit data and validator key(s)), you’ll need to run the deposit tool CLI, and should do so from an air-gapped machine. If you are not generating new keys, skip to step 2. In order to have a machine ready to go, we will need to install an OS on your new validator hardware built in step 0. We’ll be using Ubuntu Desktop. On your laptop, navigate to ubuntu.com, download the latest LTS copy of Ubuntu Desktop (as of 2025, 24.04.1 LTS). In addition, download and install the Etcher tool which will allow you to flash the operating system onto the USB drive. Lastly, download the latest version of the deposit CLI tool - you want the AMD64 version - unzip the executable file. With all the software and data assembled, run the Etcher tool to flash the USB drive with Ubuntu Desktop using your laptop. Insert the USB into your air gapped machine (built in step 0), connect the machine to a monitor via HDMI (or USB), keyboard, and mouse, and go through the installation process (which should occur automatically). Make sure to choose the minimum settings allowed so things go quickly. Once the installation is complete, remove the USB drive, and reformat the disk to exFAT so you can move the deposit CLI tool onto the USB drive. You may also want to drop a text file into the USB drive with your withdrawal address to make the next step easier. Finally, re-insert the USB drive into the NUC and move the deposit CLI tool to your desktop. Now, run the binary file in a terminal window on your Linux desktop by using the below command (easier if you have your withdrawal address handy for a copy/paste!)
sudo ./deposit new-mnemonic --num_validators {#_Validators} --chain mainnet --eth1_withdrawal_address {YourWithdrawalAddress}
Replace {#_Validators} with the number of validators you want to run (32ETH per), and {YourWithdrawalAddress} with an Ethereum address you control. NOTE: Once set, the withdrawal address CANNOT be changed, so be ABSOLUTELY SURE that it is an address you control the private keys for (or it’s a multisig and you control the private keys to the underlying wallets) and correctly specified. For example: —eth1_withdrawal_address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 DO NOT set the withdrawal address to a central exchange wallet or account. This MUST be an EOA or smart contract wallet, as ETH is not “transferred” but rather “generated” in the wallet via a state change, and that doesn’t play nicely with Coinbase/Kraken/etc. Once you execute the above steps, confirm your withdrawal address within the generated files, and provide your language preferences. You will then be asked to create the validator keystore password. Note this password on the variables document. You will need this later to import the validator(s) into the Consensus Client. Please note that this document will be referenced a number of times below, and is meant to serve as an example for a way to keep a secure paper record of your passwords, file locations, etc. Next, a seed phrase (mnemonic) will be generated. Back it up somewhere safe. This is CRITICAL. You will need this to add additional validators or to regenerate keys if lost. IF YOU LOSE THIS MNEMONIC, YOU MAY LOSE ACCESS TO THE VALIDATOR! Not your keys, not your coins. Once you have confirmed your mnemonic your validator(s) will be created. The following files will be generated and placed on your desktop.
  • 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. This is where you can triple check the withdrawal address
  • 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.
  • 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 (see here).
Now that you have generated the deposit data, keystore file(s), validator password, and the mnemonic, we can begin setting up the node. You should now copy the generated deposit and keystore files back onto your USB drive. We’ll need them later on. Note: there are no other steps in this process that require you to enter or view your mnemonic phrase, so the air-gapped portion of this process is completed. You can now plug in the Ethernet cord.

Step 2 Install Linux Server

Navigate to ubuntu.com, download the latest LTS copy of Ubuntu Server (as of 2025, 24.04.1 LTS). In addition, download and install the Etcher tool which will allow you to flash the operating system onto the USB drive. Then, run the Etcher tool to flash the USB drive with Ubuntu Server. Once that’s complete, pop the USB drive into your NUC, and restart it - also connect it to Ethernet if you haven’t already. Upon restart, hold fn+F10 in order to boot from USB. Follow the installation instructions, making sure to select the following settings:
  • When setting up hard drive settings, uncheck the LVM box else the full HD cannot be used
  • You should make sure no software is downloaded or installed aside from OpenSSH
  • Do not select “minimum” when installing the software, you will need the items downloaded and installed in the normal process
  • Name your device whatever you like - and note this on your variable document
  • Create a user when prompted to do so, and set a password for that user that’s sufficiently complex - note this on your variable document
Once this process is complete, you should be presented with a terminal window on screen. Likely, you will need to enter your password to gain access to the server. Next, update the server and install useful utilities by running the following commands:
sudo apt -y update && sudo apt -y upgrade && sudo apt dist-upgrade && sudo apt autoremove
sudo apt install -y ccze jq
sudo reboot
Note: ccze provides colorized log output, and jq is a JSON parser useful for formatting RPC responses. After rebooting:
df -h
That last command is used to ensure that the expected amount of free space on your SSD is available. If this value is less than 75% of the full size of the drive, something went wrong (likely the LVM checkbox during Linux installation), and you’ll need to start this step over.

Step 3 Secure the Server

After the updates and restart complete, it’s time to change some security settings.

Modify the Default SSH Port

Port 22 is the default SSH port and a common attack vector. Change the SSH port to avoid this. Choose a port number between 1024–49151 and run the following command to check is not already in use:
sudo ss -tulpn | grep ':{YourSSHPortNumber}'
A blank response indicates not in use, a red text response indicates it is in use: try a different port. If confirmed available, modify the default SSH port number by updating your server’s SSH config. Open the config file with the following command:
sudo nano /etc/ssh/sshd_config
Find the line Port 22 in the file. Remove the # and change the value to your chosen port. Note this port number on the variables document as you’ll need it for LAN access (we’re almost ready to do the rest from your laptop!) Press CTRL + X then Y then ENTER to save and exit. Restart the SSH service to reflect the changes.
sudo systemctl restart ssh

Configure the Firewall

Install UFW:
sudo apt install ufw
Next, apply UFW defaults:
sudo ufw default deny incoming && sudo ufw default allow outgoing
Next, we’ll need to create some custom rules to allow the requisite network activity/connections Allow Custom SSH Port:
sudo ufw allow {YourSSHPortNumber}/tcp
Deny Default SSH Port:
sudo ufw deny 22/tcp
Allow Execution Client Ports:
sudo ufw allow 30303
sudo ufw allow 30305/udp
Note: Port 30303 is used for P2P networking and Discovery v4. Port 30305 is used for Discovery v5 (improved peer discovery protocol). If you need to customize the networking ports, consult the Reth documentation for the appropriate flags and ensure those ports are allowed in your firewall. Allow Prysm:
sudo ufw allow 13000/tcp && sudo ufw allow 12000/udp
With settings imported, enable the firewall by running:
sudo ufw enable
Then run the following command to see a list of ports enabled/disabled:
sudo ufw status numbered
Finally, run the following command to get your local IP:
hostname -I
Note this on your variables document. WITHOUT disconnecting from the Node, head over to your laptop (or whichever LAN device you plan on accessing the Node from), and attempt to connect. Open a terminal window and input the following command to edit your hosts file so you can refer to the Node by the name you set it as in addition to its IP:
sudo nano /etc/hosts
When prompted, enter your password. Now, above the final ::1 localhost line, create a new line, and add the local IP of your node (see: variables), then a few spaces so you’re aligned with the localhost text, and then enter the name of the Node you used above (see: variables). Now, let’s test to see if you can SSH into your Node. Simply open a new terminal window, and enter the following command, subbing in the name of the Node, user, and the SSH port (see: variables).
ssh {yourusername}@{node_name OR node_IP} -p {YourSSHPortNumber}
Enter your password when prompted. If you are able to log in and see {user}@{node_name}, you’re set! Once you see this, you can disconnect the Node from the monitor and keyboard, and can perform the remainder of tasks from any laptop or computer on the same LAN using the command above to SSH in. The Node only needs to remain connected to power and ethernet (powered via UPS if possible).

Configure Fail2Ban

Fail2Ban is an intrusion-prevention program that scans log files and bans IPs that show malicious activity. If a certain number of failed logins are detected from a specific IP address (within a specified amount of time), that IP address is blocked. This service provides basic protection against brute-force attacks, and can be configured to ignore local IPs. Install fail2ban, and enable and start the service:
sudo apt install -y fail2ban && sudo systemctl enable fail2ban
Create the configuration file for the service:
sudo nano /etc/fail2ban/jail.local
Then, paste the following configuration into the file:
[sshd]
enabled = true
port = {your SSH port}
filter = sshd
logpath = /var/log/auth.log
maxretry = 4
# whitelisted IP addresses
ignoreip = 192.168.1.0/24 127.0.0.1/8
NOTE: The inclusion of ignoreip = 192.168.1.0/24 127.0.0.1/8 is to allow local IPs to connect (and potentially get passwords wrong) without being blocked. Press CTRL + X then Y then ENTER to save and exit. Reload systemd to reflect the changes and start the service. Check the status to make sure it’s running correctly:
sudo systemctl daemon-reload
sudo systemctl start fail2ban
sudo systemctl status fail2ban
If it says “active (running)” in green text, you’ve done it! Press Q to quit (this will not affect the fail2ban service)

Disable Root Access

It is best practice to not log in as root in order to maintain security and follow the principle of least-privilege. First, check to ensure another user besides root can run the sudo command (if you’ve gotten this far, this is just a double check step). Let’s first check sudo access:
sudo -l
If you see the text “User {your-username} may run the following commands on {device-name}: (ALL:ALL) ALL” you have sudo privileges with your current user. Now, lock the root account:
sudo passwd -l root
You should see the text “passwd: password expiry information changed.” If for any reason you need to unlock the root account:
sudo passwd -u root

Step 4 Configure Port Forwarding

From your laptop (or whichever device on the local network you plan on accessing the Node from), log into your router to edit the port forwarding settings. While access is different for each network set up and router, usually you can access your router at 192.168.1.1 or a similar local IP address. Occasionally, instructions can be found on the router itself. Once you’ve logged in to your router, edit the port forwarding settings for your Node to forward the following ports: Reth (Execution Client):
  • Port 30303 - TCP and UDP - P2P networking and Discovery v4
  • Port 30305 - UDP only - Discovery v5 (improved peer discovery)
Prysm (Consensus Client):
  • Port 13000 - TCP and UDP - P2P networking
  • Port 12000 - UDP only - QUIC discovery
These are the same ports we allowed above in the Secure the Server step. Port forwarding these will make it easier for both the execution client (Reth) and consensus client (Prysm) to find suitable peers and accept inbound connections. Failure to do so may result in sub-optimal syncing and significantly reduced peer discovery (especially for Prysm, which targets 70 peers).

Step 5 Configure Timekeeping

Running validators against a blockchain requires accurate timekeeping in order to ensure proper synchronization with the network. Validators need time accuracy within 0.5 seconds for proper attestations. While Ubuntu has time synchronization built in using systemd-timesyncd, we’ll use chrony instead, which provides more accurate time synchronization - critical for validators. Install chrony (this will replace systemd-timesyncd):
sudo apt-get install chrony -y
Verify chrony is running and check synchronization sources:
sudo systemctl status chrony
Check the time servers chrony is using:
chronyc sources
You should see multiple time servers with various symbols (^*, ^+, ^-, ^?) indicating their status. Check synchronization accuracy:
chronyc tracking
Important values to check:
  • Leap status: Should say “Normal” (after a few minutes)
  • System time: Should show offset in milliseconds (< 100ms is good, < 10ms is excellent)
  • Stratum: Should be 2-4 (lower is better)
Note: Right after installation, chrony needs 5-10 minutes to synchronize with time servers. The “Leap status” will show “Not synchronized” initially - this is normal. Optionally, set your timezone (affects log timestamps only, doesn’t impact validation):
sudo dpkg-reconfigure tzdata
Select your geographic region and city from the menu.

Step 6 Generate Client Auth Secret

On the server, communication between the Execution and Consensus clients is secured using a JSON Web Token (JWT) authentication scheme. The JWT is represented by a file that contains a randomly generated 32-byte hex string. The Execution and Consensus clients each make use of the file for message authentication. First, create a directory to store this file:
sudo mkdir -p /var/lib/jwtsecret
Then, generate the JWT file using openssl:
openssl rand -hex 32 | sudo tee /var/lib/jwtsecret/jwt.hex > /dev/null
Finally, check that the file was created as expected:
sudo nano /var/lib/jwtsecret/jwt.hex
Press CTRL+X to exit

Step 7 Install Reth

Time to install Reth! All commands provided below are based on the v1.9.3 version of Reth (current as of Nov 2025), but should be adjusted based on whatever the latest version of Reth is here - simply expand the Assets section and copy the URL for the ‘reth-v…-x86_64-unknown-linux-gnu.tar.gz’ file. Note that MANY commands below will need to update if this version is updated. Full Node vs Archive Node: This guide configures Reth as a full node (using the --full flag), which is the optimal configuration for validators:
  • Full Node: Stores recent state and enough history to validate the chain. Syncs in hours (especially with snapshots), requires ~1-2TB storage, and is sufficient for all validation operations.
  • Archive Node: Stores complete historical state since genesis. Takes weeks to sync, requires 3-4TB+ storage, and is only needed for specialized applications like block explorers or historical data analysis.
For validators, always use a full node. Archive nodes provide no benefit for validation and waste time and resources. Curl the Reth build from the above link:
cd ~
curl -LO https://github.com/paradigmxyz/reth/releases/download/v1.9.3/reth-v1.9.3-x86_64-unknown-linux-gnu.tar.gz
Extract the files from the archive and copy to the /usr/local/bin directory. The Reth service will run it from there. Modify the file name to match the downloaded version:
tar xvf reth-v1.9.3-x86_64-unknown-linux-gnu.tar.gz
sudo cp reth /usr/local/bin
Clean up the files. Modify the file name to match the downloaded version:
cd ~
rm reth-v1.9.3-x86_64-unknown-linux-gnu.tar.gz
Reth will be configured to run as a background service. Create an account for the service to run under. This type of account can’t log into the server:
sudo useradd --no-create-home --shell /bin/false reth
Create the data directory. This is required for storing the Ethereum blockchain data:
sudo mkdir -p /var/lib/reth
Set directory permissions. The Reth user account needs permission to modify the data directory:
sudo chown -R reth:reth /var/lib/reth
Create a home directory for the Reth user. This is required for Reth’s logging functionality:
sudo mkdir -p /home/reth
sudo chown reth:reth /home/reth
Create a systemd service config file to configure the service:
sudo nano /etc/systemd/system/reth.service
Then, paste the following configuration into the file:
[Unit]
Description=Reth Execution Client (Mainnet)
After=network.target
Wants=network.target

[Service]
User=reth
Group=reth
Type=simple
Restart=always
RestartSec=5
TimeoutStopSec=900
KillSignal=SIGINT

ExecStart=/usr/local/bin/reth node \
  --chain mainnet \
  --full \
  --datadir /var/lib/reth \
  --authrpc.jwtsecret /var/lib/jwtsecret/jwt.hex \
  --authrpc.addr 127.0.0.1 \
  --authrpc.port 8551 \
  --port 30303 \
  --discovery.port 30303 \
  --enable-discv5-discovery \
  --discovery.v5.port 30305 \
  --max-outbound-peers 35 \
  --max-inbound-peers 35 \
  --http \
  --http.addr 127.0.0.1 \
  --http.api eth,net,web3

[Install]
WantedBy=default.target
IMPORTANT NOTES:
  • --full flag: Runs Reth as a full node (not archive). For validators, you only need a full node which stores recent state. Archive nodes store all historical state since genesis and are unnecessary for validation, taking significantly longer to sync (weeks vs hours) and requiring much more disk space.
  • TimeoutStopSec=900: Allows the Reth service 15 minutes to write cached data to disk on clean shutdown (important for large databases).
  • KillSignal=SIGINT: Explicitly tells systemd to use SIGINT for graceful shutdown (same as Ctrl+C).
  • Port configuration: Explicitly sets P2P networking ports to 30303 (default) for better peer discovery. This is the default, but better to make explicit.
  • Discovery v5: Enables the newer, more resilient peer discovery protocol on port 30305 UDP. Improves peer finding and network connectivity.
  • Peer limits: Limits connections to 35 outbound + 35 inbound (70 total) to reduce bandwidth and resource usage while maintaining excellent connectivity. For gigabit home validators, this balances network contribution with efficiency.
Press CTRL + X then Y then ENTER to save and exit. DO NOT add additional pruning flags (--prune.accounthistory.full, --prune.storagehistory.full, etc.) as they will conflict with the --full flag’s configuration and cause the pruner to fail. The --full flag handles all pruning automatically.

Optional: Use Merkle Snapshot for Faster Initial Sync

Why use a snapshot? Syncing Reth from genesis can take days even with a full node. Using a recent snapshot from Merkle.io can reduce initial sync time to just a few hours. Merkle.io publishes Reth snapshots every Monday and Thursday at downloads.merkle.io. Using a snapshot is particularly recommended for new installations or after database corruption.
Archive Node Snapshot Notice: The Merkle snapshot is an archive node snapshot, not a full node snapshot. This means:
  • Faster sync: You’ll catch up with the chain much faster than syncing from genesis
  • Larger storage: The resulting database will be larger than a pure full node sync (~2TB+ vs ~1TB)
  • Pruning in progress: The Reth team is actively working on improved pruning that will make it easy to convert from archive to full node storage
As of December 2025, Georgios Konstantopoulos (Reth/Paradigm) noted in the Reth Telegram that upcoming changes will “basically remove pruning as an issue altogether” with different storage engines for different workloads. Our recommendation is to use the snapshot to speed up your initial sync, and trust that Reth’s pruning improvements will soon make it trivial to reclaim the extra space.For now, ensure you have sufficient storage (>=4TB recommended) when using the snapshot approach.
To use a Merkle snapshot: First, install required dependencies:
sudo apt-get update
sudo apt-get install -y lz4 tmux
IMPORTANT: Use tmux to run the download to prevent it from being killed if your SSH connection drops. Start a tmux session:
tmux new -s reth-download
Then, inside the tmux session, stream download and extract the snapshot:
cd /var/lib/reth
sudo -u reth bash -c 'wget -O - https://downloads.merkle.io/reth-YYYY-MM-DD.tar.lz4 | tar -I lz4 -xvf -'
Replace YYYY-MM-DD with the latest snapshot date (most recent Monday or Thursday). Check Merkle’s snapshot page for the current available dates. Example for January 1, 2026:
cd /var/lib/reth
sudo -u reth bash -c 'wget -O - https://downloads.merkle.io/reth-2026-01-01.tar.lz4 | tar -I lz4 -xvf -'
If download gets interrupted: The streaming method is NOT resumable. If interrupted, you must clean up and start over:
sudo rm -rf /var/lib/reth/*
# Then rerun the full streaming command from the beginning
This is why tmux is critical - it keeps the process running even if SSH disconnects. Tmux controls:
  • Detach from session (leaves download running): Press Ctrl+B then D
  • Reattach to session: tmux attach -t reth-download
  • Monitor progress in another terminal: watch -n 30 'df -h /var/lib/reth' or tail -f /var/lib/reth/wget-log or watch -n 60 'du -sh /var/lib/reth'
This command will:
  • Download the Merkle snapshot
  • Extract it directly into your data directory (streaming, no temp file)
  • Set proper ownership as the reth user
Once the snapshot extraction completes, proceed with starting the service as normal. If not using a snapshot: Reth will sync from genesis with the --full flag, which takes 1-3 days but results in a smaller database (~1TB). Skip the snapshot download and proceed directly to starting the service. This is a valid approach if you have time and want to minimize storage usage, though the Merkle snapshot approach is faster and Reth’s upcoming pruning improvements will eventually make the difference negligible.

Start Reth Service

Reload systemd to reflect the changes and start the service. Check the status to make sure it’s running correctly:
sudo systemctl daemon-reload
sudo systemctl start reth
sudo systemctl status reth
If it says “active (running)” in green text, you’ve done it! Press Q to quit (this will not affect the Reth service) The sync will begin immediately. Use the journal output to follow the progress or check for errors by running the following command.
sudo journalctl -fu reth | ccze
Press CTRL+ C to exit (will not affect the Reth service). Note (initial sync / snapshot troubleshooting): During the initial sync, Reth may occasionally hit an error during pruning or appear to get “stuck”. If you’re monitoring logs and see errors, Reth generally does a good job persisting progress. In many cases, a simple restart is enough to resume work:
sudo systemctl restart reth
Next, enable the Reth service to automatically start on reboot:
sudo systemctl enable reth
Finally, stop and start reth so it’s using all the latest config information:
sudo systemctl stop reth
sudo systemctl start reth
Now that Reth is all set up, you can check sync status and connected peers using JSON-RPC calls. Check the sync status using curl:
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545
You can also check the count of connected peers by running:
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' http://localhost:8545
After running Reth for a few minutes, check the peer count to ensure it’s greater than 0.

Step 8 Install Prysm

The Prysm Consensus Client is made up of two binaries that provide the functionality of the beacon node and validator, respectively. This step will download and prepare the Prysm binaries. Time to install Prysm! All commands provided below are based on the 7.1.2 version of Prysm (current as of 1/7/26), but should be adjusted based on whatever the latest version of Prysm is here. Note that many commands below will need to update if this version is updated. In the Assets section (expand if necessary) copy the download links to the beacon-chain-v…-linux-amd64 file and the validator-v…-linux-amd64 file. Be sure to copy the correct links. Download the binaries using the commands below. Modify the URL to match the copied download links:
cd ~
curl -LO https://github.com/OffchainLabs/prysm/releases/download/v7.1.2/beacon-chain-v7.1.2-linux-amd64
curl -LO https://github.com/OffchainLabs/prysm/releases/download/v7.1.2/validator-v7.1.2-linux-amd64
Now, let’s rename the files and make them executable and copy them to the /usr/local/bin directory. The Prysm services will run them from there. Modify the file names to match the downloaded version:
mv beacon-chain-v7.1.2-linux-amd64 beacon-chain
mv validator-v7.1.2-linux-amd64 validator
chmod +x beacon-chain
chmod +x validator
sudo cp beacon-chain /usr/local/bin
sudo cp validator /usr/local/bin
Finally, clean up the files
rm beacon-chain && rm validator

Step 9 Configure the Beacon Node Service

Set up an account for the services to run under (note: this type of account cannot log into the server):
sudo useradd --no-create-home --shell /bin/false prysmbeacon
Next, set up the directories and permissions for prysmbeacon:
sudo mkdir -p /var/lib/prysm/beacon
sudo chown -R prysmbeacon:prysmbeacon /var/lib/prysm/beacon
Next, create and configure the service file:
sudo nano /etc/systemd/system/prysmbeacon.service
And then paste the following data into the file:
[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 \
  --blob-storage-layout=by-epoch
Environment=USE_PRYSM_MODERN=true
[Install]
WantedBy=multi-user.target
Make sure to replace {FeeRecipientAddress} with the address you’d like to receive the validator tip fees. Make sure to replace {CheckpointSyncURL} (x2) with a valid checkpoint sync URL see here for more information - be sure to select a Mainnet endpoint. Press CTRL + X then Y then ENTER to save and exit. Reload systemd to reflect the changes and start the service. Check the status to make sure it’s running correctly:
sudo systemctl daemon-reload
sudo systemctl start prysmbeacon
sudo systemctl status prysmbeacon
If it says “active (running)” in green text, you’ve done it! Press Q to quit (this will not affect the PrysmBeacon service) - the sync has already begun. Use the journal output to follow the progress or check for errors by running the following command:
sudo journalctl -fu prysmbeacon | ccze
Finally, enable the service to automatically start on reboot:
sudo systemctl enable prysmbeacon

Step 10 Configure the Validator Service

Set up an account for the services to run under (note: this type of account cannot log into the server):
sudo useradd --no-create-home --shell /bin/false prysmvalidator
Create a directory to store the validator data:
sudo mkdir -p /var/lib/prysm/validator
Next, set up the permissions for prysmvalidator:
sudo chown -R prysmvalidator:prysmvalidator /var/lib/prysm/validator
Next, create and configure the service file by running:
sudo nano /etc/systemd/system/prysmvalidator.service
And then paste the following data into the file:
[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} \
  --accept-terms-of-use
[Install]
WantedBy=multi-user.target
Make sure to replace {FeeRecipientAddress} with the address you’d like to receive the validator tip fees. Press CTRL + X then Y then ENTER to save and exit. Reload systemd to reflect the changes and start the service. Check the status to make sure it’s running correctly:
sudo systemctl daemon-reload
sudo systemctl start prysmvalidator
sudo systemctl status prysmvalidator
If it says “active (running)” in green text, you’ve done it! Press Q to quit (this will not affect the PrysmValidator service) - the sync has already begun. Use the journal output to follow the progress or check for errors by running the following command:
sudo journalctl -fu prysmvalidator  | ccze
Finally, enable the service to automatically start on reboot:
sudo systemctl enable prysmvalidator
Now that the beacon-chain, validator, and execution client software is all set up, you should run the following commands to check sync status: Check the sync status using JSON-RPC (it will work now!):
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545
You can also check the peer count:
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' http://localhost:8545

Step 11 Import Validator Keys

To import the keys into Prysm, we’ll need to get them onto the same machine.

Copy Validator Keystore File(s) to Server

Before we transfer over the Validator Keystore file(s), we’ll need to create a place to store them:
sudo mkdir -p $HOME/staking-deposit-cli/validator_keys
Next, ensure your user has control of that folder:
sudo chown -R {yourusername}:{yourusername} $HOME/staking-deposit-cli/validator_keys
Now, plug the USB into your Node from Step 1 (remember Step 1?). Back in terminal, check to see if the drive is recognized:
sudo fdisk -l
This will list all available disks and partitions. You should see the USB drive listed as a block device, such as /dev/sdb or /dev/sdc, and specific drives underneath. Look for your USB drive and note its code (e.g. sdb1 or sdc1). Create a mount point for the USB drive by running the following command:
sudo mkdir /mnt/usb
Then, mount the USB drive to the mount point, replacing sdb1:
sudo mount /dev/{sdb1} /mnt/usb
Check that the USB drive is connected by looking at all mounted drives:
df -h
Now, it’s time to copy the files from the USB to the proper folders within the Node. First, let’s get a list of all files by running:
ls /mnt/usb
This should show something like the following
deposit_data-1682896118.json keystore-m_12381_3600_0_0_0-1682896115.json
Note that while you will only ever have 1 deposit file (regardless of the amount of validators), you will have 1 keystore file for each validator you are setting up on the Node. You only need to move the keystore file(s) to the validator_keys folder. Now that we have the file names, we issue a command to copy those files from /mnt/usb to $HOME/staking-deposit-cli/validator_keys:
cp /mnt/usb/{keystore-filename.json} $HOME/staking-deposit-cli/validator_keys/
If you have more than 1 validator key, you will need to rerun the above command once for each. BE SURE TO ONLY HAVE 1 COPY OF EACH VALIDATOR KEY. Before unmounting the USB, let’s verify that the files were placed into the folder correctly by running:
ls $HOME/staking-deposit-cli/validator_keys/
If you see 1 copy of each validator key you intended to move, you’re all set! Now, unmount the USB:
sudo umount /mnt/usb

Import the Validator Keystore Files into Prysm

REMEMBER: If you import the same keystore twice OR run the same keystore on multiple nodes at the same time YOU WILL GET SLASHED! Now we will run the validator import process. You will need to provide the directory where the generated keystore-[..].json files are located. E.g. $HOME/staking-deposit-cli/validator_keys (as determined above in this step)
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 systemctl start prysmvalidator
You will then be presented with a terms of use which you will need to accept in order to continue. Following your acceptance, you’ll be prompted to create a wallet password. Note that this is a different password than the validator password set in step 1 when generating the keystore files. This password will be used by Prysm to decrypt the validator files. Note this password on the variables document. After setting your wallet password, you’ll be asked to enter the validator keystore password, this is the one referenced above and created during step 1. Once this password is entered, all validator keystore file(s) will be imported.

Create a Wallet Password File

Create a file to store the wallet password so the Prysm validator service (set up below) can access the wallet without you having to supply the password:
sudo nano /var/lib/prysm/validator/password.txt
This will create a new text file and open a nano text editor. Type your password you just created for your validator wallet into the file. Press CTRL + X then Y then ENTER to save and exit. Let’s now check that Prysm can see the password:
sudo systemctl status prysmvalidator
Before continuing to the deposit step, let’s do one more permissions check:
sudo chown -R prysmvalidator:prysmvalidator /var/lib/prysm/validator && sudo chown -R {yourusername}:{yourusername} /var/lib/prysm/validator
sudo systemctl daemon-reload

Step 12 Fund the Validators

Now that the Consensus Client is up and running, to actually begin staking on the Ethereum network you will need to deposit ETH to fund your validators. Note: DO NOT continue until your execution and consensus clients are fully synced. If they have not and your validator(s) become active on the network, you would be subject to inactivity penalties. You can determine sync status by checking the status of the Beacon Chain or through JSON-RPC calls to Reth, both methods are described above. With the deposit file in hand and an EOA with 32ETH per validator + some spare ETH for gas, head to the Ethereum Launchpad Click on Become a Validator, click through the warning steps and continue through the screens until you get to the Generate Key Pairs section. Select the number of validators you are going to run. Choose a value that matches the number of validators you created in Step 1. Scroll down, check the box if you agree, and click Continue. You will be asked to upload the deposit_data-[timestamp].json file. You generated this file in Step 1. (Note: There are no security concerns copying the file or having access to it on a public computer). Double check that inside the file the withdrawal address is set correctly. When satisfied, browse or drag the file to upload and click continue. Connect your wallet. Choose MetaMask (or one of the other supported wallets), log in, select the account where you have your ETH and click Continue. Your MetaMask balance will be displayed. The site will allow you to continue only if you have selected Mainnet and you have a sufficient ETH balance. A summary shows the number of validators and total amount of ETH required. Tick the boxes if you agree and click continue. If you are ready to deposit click on Initiate All Transactions. This will pop open MetaMask (or one of the other supported wallets) where you can confirm and broadcast each transaction. Once all the transactions have successfully completed you are done! WOOOOOOO! Congratulations you have deposited your stake!

Check the Status of Your Validator Deposits

Newly added validators can take a while to activate. You can check the status of your keys with these steps:
  1. Copy your wallet address used to make the deposit
  2. Go to Beaconcha.in
  3. Search for your key(s) using your wallet address
Digging into a specific validator we can see a Status section that provides an activation estimate time for each validator. That’s it. You now have a functioning Execution and Consensus client and the staking deposit done. Once your deposit is active you will automatically begin staking and earning rewards. Probably a good time to touch grass.

Clean Up validator_keys

While not essential as you can always regenerate them using your mnemonic, it’s good practice to back up the validator_keys directory (typically $HOME/staking-deposit-cli/validator_keys) on a USB for emergencies. After ensuring you have a backup of these files as described in Step 1 Generate Staking Data, you can safely remove the validator_keys directory. Note that this will not impact your existing validators, as in Step 11 Import Validator Keys the validator_keys are imported into the validator instance. The copies referenced above are “extra”:
sudo rm -r $HOME/staking-deposit-cli/validator_keys