I’ve experimented with running local nodes before, but the underpowered hardware I was using made that difficult. I recently upgraded everything with various goals in mind, but an important one was the ability to host multiple local nodes for the blockchains I’m interested in (Ethereum, Avalanche, Fantom).
I don’t have enough storage space to run all three at once (yet…) so I prioritized getting the Ethereum node working first.
To my surprise, setting up an Ethereum node is actually quite easy and fast. It also has the lightest technical burden of the three chains I mentioned.
It’s been running for a few days and has a strong connection to the network, so much so that it’s consistently overperforming against Infura (the most popular Ethereum RPC).
There are several clients that you can run. The most popular is Go Ethereum (aka geth) followed by Erigon. I have chosen to run a geth node, largely because of its already massive presence. Everyone is familiar with geth, so I will be, too.
What is a Node?
A node is a full participant in the blockchain network. It connects to other nodes on the peer-to-peer network, transmits and receives new blocks and pending transactions, and optionally attempts to confirm (mine) those transactions and earn block rewards.
Everyone here is familiar with an RPC at this point. We are used to interacting with a blockchain network through the RPC as a trusted middleman.
If you use Metamask without customizing your network list, you will use an Infura RPC endpoint.
Why Run a Node?
There are philosophical and practical reasons to run a node.
Adding a node to the Ethereum network strengthens it. While it is still configured for proof-of-work consensus, each additional node make it harder to attack.
Putting my tinfoil hat on for a moment, how can you truly believe your RPC? There is no practical way to verify the data returned by your RPC is any good. It says my balance is 100 Ether, so my balance must be 100 Ether, right? Maybe.
This very interesting thread suggests that RPC providers can see a lot more than they should.
In the MEV world there are other practical considerations.
For one, running a local node means that I have zero latency when reading state from the blockchain. I have some transaction delay when I submit a transaction outward, but I had that with an RPC anyway.
Also, with a local node I don’t have to deal with request limits. When working with Infura/Moralis/Ankr, I have to take care not to exceed some arbitrary limit (5 request/second, 100 requests/minute, etc). With a local node I can just blast as many calls as I want.
Installation Options
Recall that I’m running Linux on my workstation (specifically Pop!_OS 22.04 LTS), so I have two primary means to install geth:
PPA — A repository maintained by the Ethereum team that provides packages installed via
apt
Docker — A containerized version maintained by the Ethereum team
I have experimented with both and found Docker to be the better choice simply for the ease of starting (or not starting) the node at boot time.
Docker Installation
If you don’t have Docker on your system, look up the installation instructions to install Docker Community Edition (CE), which is the fully open sourced version without the commercial licensing.
For Ubuntu installs, it’s quite easy:
sudo apt install docker.io docker-compose
For other distributions, go to the link above and follow the instructions. Or consult the documentation for your distribution’s package manager. There may be a native package for Docker CE that you can install without any extra trouble.
With that done, make sure Docker is running:
devil@hades:~$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2022-07-24 10:05:52 PDT; 1 day 10h ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
[...]
I prefer to put all of my docker container information inside a dedicated /docker
directory, with containers in their own folder.
My geth container is defined in docker-compose.yml
inside /docker/geth
:
version: "2"
services:
geth:
image: ethereum/client-go:stable
container_name: geth
restart: unless-stopped
network_mode: host
volumes:
- data:/root
command:
- --ws
- --ws.addr=0.0.0.0
- --http
- --http.addr=0.0.0.0
# - --http.corsdomain=[read below]
volumes:
data:
This file defines some important options: --ws and --http
enable the HTTP and WebSocket endpoints, --ws.addr
and --http.addr
enable connection to those endpoints from all other IPs (by default, only localhost is supported). The documentation is vague on this topic, but I believe that the value is a netmask (default 255.255.255.0)
If you are running this on a larger network, consider setting the values more restrictively.
You can also choose to use the IPC feature by attaching it outside the container in a secured location.
Networking
Geth does peer-to-peer host discovery on port 30303 (TCP and UDP).
Lighthouse does peer-to-peer host discovery on port 9000 (TCP and UDP).
You may have to forward these ports in your router if you have trouble connecting to peers quickly or maintaining sync.
Startup
After you’ve chosen your values, start the container with sudo docker-compose up -d
, then watch the logs using sudo docker-compose logs -f
.
It will begin to synchronize with the network, connect to other peers, download and verify every block from genesis until current.
This will take a day or so to complete, and might complete faster if you have a strong machine and internet connection.
I am fully synced and my data directory consumes roughly 670 GB.
Console Access
If you need to access the geth console, you can attach to it inside the Docker container using docker-compose exec [container name] geth attach
.
Preparing For The Merge
The Ethereum mainnet transition to proof-of-stake consensus will be activated when the TTD (Total Terminal Difficulty) reaches 58750000000000000000000. At that time, you will need a consensus client running alongside geth to maintain connectivity to the network.
There are several consensus clients available, and I will demonstrate how to connect Lighthouse with Geth here.
Lighthouse publishes a Docker container, which makes adding a consensus client to geth very simple. Lighthouse can be added to the docker-compose.yml
file above very simply, by modifying the compose file as follows:
version: "2"
services:
geth:
image: ethereum/client-go:stable
container_name: geth
restart: unless-stopped
#network_mode: host
ports:
- "8545:8545"
- "8546:8546"
- "30303:30303/tcp"
- "30303:30303/udp"
volumes:
- data:/root
command:
- --ws
- --ws.addr=0.0.0.0
- --http
- --http.addr=0.0.0.0
# - --http.corsdomain=[read below]
- --authrpc.addr=0.0.0.0
- --authrpc.port=8551
- --authrpc.vhosts=*
lighthouse:
image: sigp/lighthouse:latest
container_name: lighthouse
restart: unless-stopped
ports:
- "9000:9000/tcp"
- "9000:9000/udp"
- "127.0.0.1:5052:5052"
volumes:
- lighthouse:/root/.lighthouse
- data:/geth/:ro
command:
- lighthouse
- --network=mainnet
- beacon
- --http
- --http-address=0.0.0.0
- --execution-endpoint=http://geth:8551
- --execution-jwt=/geth/.ethereum/geth/jwtsecret
- --checkpoint-sync-url=https://beaconstate.info/
volumes:
data:
lighthouse:
Summary of changes:
Create a service for the Lighthouse consensus client, running on port 9000 (both TCP and UDP, forward in your router as needed).
Expose geth’s
data
directory (read-only) so it can access the JWT secret (an on-demand generated key for connecting to the RPC auth service).Enable geth’s authrpc service, authenticated with a JWT secret only readable from the shared file inside geth’s
data
directory.
Lessons Learned
I have learned a few things from readers needing help on their specific platform. Here are the big ones:
Host networking is not supported on Windows or Mac, so drop that option and forward ports the usual way
Connecting geth and lighthouse is not easily done if both containers are using host networking. I recommend using standard NAT networking, and have edited the docker-compose.yml file above to reflect this configuration.
Docker for Mac has an extra setting that limits the disk space avilable to a container. By default it is too low to hold the geth and lighthouse databases and geth will continuously restart and throw errors. Provide a minimum of 650 GB for geth and 125 GB for lighthouse to ensure your node can bootstrap and run.
This was last updated Oct 1, 2022. The disk space values will increase as the blockchain height increases, so adjust upwards as needed !
Connecting To Metamask
Metamask is an extremely popular web browser plugin for interacting with RPCs. By default, geth will reject HTTP connections from external domains. To use Metamask with your local node, you first need to enable access from one or more domains.
The “easy” (and probably unsafe) choice is to allow access from any domain, which you can enable by adding this to the command:
section above
- --http.corsdomain=*
I prefer to allow access to Metamask only. The domain for a Firefox plugin is moz-extension://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
and the domain for a Chrome plugin is chrome-extension://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Chrome plugin IDs are common across installs, so you can use the official extension ID chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn
as described HERE.
Firefox uses unique plugin IDs, so you need to look a little harder. You can find your unique plugin ID for Firefox by pasting about:debugging#/runtime/this-firefox
into your address bar and finding your Metamask section. Copy the “Internal UUID” value and replace the xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
characters above.
You may add multiple domains with a comma separation. Here is an example of adding Metamask domains for both Firefox and Chrome:
- --http.corsdomain=moz-extension://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn
Shutdown and Restart
By default, Docker allows a container 10 seconds to terminate after receiving a SIGTERM
signal. If it is still running after 10 seconds, it will send a SIGKILL
signal, which kills all running processes completely.
Geth needs more than 10 seconds to cleanly stop, so if you need to stop or restart the container, I recommend using the -t
option with docker-compose
to allow extra time for it to stop cleanly. Give it a few minutes at least, though an impractically high value (-t 9999999
for example) will ensure geth has enough time to cleanly stop. This avoids a very long data scrubbing and verification process on the next startup.
Moving Forward
With that running, you’ll now to able to access the Ethereum network directly via your local node by pointing your Metamask, Ledger, Trezor, etc. at the HTTP/WS ports.
I’m preparing you by setting up an Ethereum node because the next series of posts will introduce and connect you to FlashBots, then we will start building some Ethereum bots to MEV with the big boys.
Hello, I am having trouble connecting brownie to my local node. Although it seems to be connected, when I try to fetch a contract from the explorer it tells me there is no contract found...
ContractNotFound: No contract deployed at 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
This is the WETH contract. Works fine when I use infura. One thing to note, my consensus node is still syncing, but the execution node says it is synced.
I found the project and documentation at eth-docker.net to be super useful here for running an ETH2 node using docker. After struggling with trying to get independent docker images running for geth and lighthouse - and trying to run directly on Ubuntu through native ppm resources - I found this all-in-one solution works incredibly well. Well documented. Comes with Grafana built in for monitoring performance and sync.
Just don't forget to move your docker volume location to a large SSD for performant and voluminous storage - don't let it fill up /var/lib!
NOTE: I don't work for this company, I have no affiliation with eth-docker.net - just a hobbyist with a red spot on his head from banging it into a wall for days trying to get a node running smoothly, saved by this package of containers to run an ETH2 node!
Hope this helps...