Applications AWS

Go-Ethereum (Geth) 1.17.10 on AWS User Guide

| Product: Go-Ethereum (Geth) 1.17.10 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Go-Ethereum (Geth) 1.17.10 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg GIS, health and research applications listing on AWS Marketplace; you choose it as the software version when you launch.

Go-Ethereum (geth) is the Go implementation of Ethereum from the go-ethereum project. It is an Ethereum execution client: it connects to the Ethereum peer-to-peer network, downloads and verifies blocks, keeps the state, and serves the JSON-RPC API that wallets, indexers and applications use.

What this image does, and what it does not. Since the Merge, an Ethereum node is two programs: an execution client (geth) and a consensus client (a beacon node such as Lighthouse, Prysm, Teku, Nimbus or Lodestar) that tells geth which chain head to follow, over the authenticated engine API on port 8551. This image ships geth only. Out of the box geth starts, finds peers and answers RPC calls, but it does not follow the chain head until you run a consensus client next to it (Step 7). A full mainnet node also needs a large data volume (Step 8).

The cloudimg image is a hardened, ready-to-use geth node:

  • RPC never open to the network. JSON-RPC (8545), WebSocket (8546) and the engine API (8551) listen on 127.0.0.1 only, and only the eth, net and web3 namespaces are served over HTTP and WebSocket. No account is unlocked, and the personal, admin, debug, miner and txpool namespaces are not served over the network listeners. A request with a foreign Host header or a cross-site WebSocket Origin is refused. You reach the RPC from your computer over an SSH tunnel (Step 6).
  • An identity per instance. The image ships no node key and no JWT secret. geth generates its peer-to-peer node key and the engine API's jwt.hex on your instance at its first start, so two instances never share an enode identity or a JWT secret. If either file were ever found before the first boot, the first-boot service destroys it.
  • Chain data on its own volume. /var/lib/geth is a separate gp3 EBS volume (ext4, mounted by filesystem UUID), shipped empty. You set its size at launch or enlarge it later, and the instance grows the file system online.
  • An outbound block. geth itself may not open connections to the EC2 metadata service, to link-local, private, CGNAT and reserved ranges, or to this instance's own VPC ranges (systemd IPAddressDeny). It still reaches public Ethereum peers. The instance reads its VPC ranges from the metadata service at every boot and geth does not start until it has them.
  • A checked configuration. Before every start, a guard checks the binary against its pinned SHA-256, the data volume, the four tunables in /etc/default/geth and the effective systemd configuration. A drop-in that would open the RPC to the network, unlock an account or turn on an unsafe flag is refused, and geth stays stopped (exit 78) instead of restarting in a loop.
  • An unprivileged service. geth runs as the geth account (no shell, no password, no sudo) under systemd with NoNewPrivileges, a read-only operating system, no capabilities, MemoryDenyWriteExecute and core dumps off.
  • The official release. /usr/local/bin/geth is the go-ethereum project's own linux-amd64 release build of 1.17.10 (commit 1df30b18), verified by its published signature and pinned by SHA-256. The corresponding source and every third-party licence are on the instance (Step 12).

What is on the image: geth 1.17.10, cloud-init and the AWS Systems Manager agent on Ubuntu 24.04. No web server and no consensus client.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with internet access (geth must reach public peers).
  • A security group that allows TCP 22 (SSH) from your own address and TCP and UDP 30303 (Ethereum peer-to-peer) from anywhere. Without 30303 geth still connects out to peers, but other nodes cannot connect to it. Never open 8545, 8546 or 8551.
  • IMDSv2 enabled (the default for this AMI). The instance reads its VPC ranges and its public address from the metadata service at every boot; with the metadata service switched off, geth does not start (see Step 10).
  • A data volume sized for your network. The AMI's data volume is only 20 GiB: enough to try the image, not enough for mainnet. A synced mainnet node needs at least 650 GiB for geth alone, and 1 to 2 TiB is the practical size with room to grow, so size the /dev/sdf volume at launch (Step 1) or enlarge it before you start a consensus client (Step 8). A testnet (sepolia, hoodi) needs far less. The image ships set to mainnet with up to 50 peers; on the 20 GiB volume, change the network or keep the instance for evaluation only.
  • Data transfer costs. geth uploads blocks and transactions to its peers, and that traffic is billed as EC2 data transfer out to the internet. A mainnet node with 50 peers can send hundreds of GB a month. Lower GETH_MAXPEERS to reduce it (Step 9), and watch the instance's NetworkOut metric in CloudWatch.
  • Memory: geth uses about 1 GiB of cache plus its own working set by default. m5.large (8 GiB) is the minimum we recommend; a busy RPC node or a node with a consensus client on the same instance needs 16 GiB or more.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Go-Ethereum (Geth) 1.17.10 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large or larger, your key pair, and a security group as above. Under Storage, set the size of the second volume (/dev/sdf, the chain data) to what your network needs.

With the AWS CLI (replace the values in angle brackets; the example sets a 1,500 GiB data volume):

aws ec2 run-instances --image-id <ami-id-from-the-listing> --instance-type m5.large \
  --key-name <your-key-pair> --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --block-device-mappings 'DeviceName=/dev/sdf,Ebs={VolumeSize=1500,VolumeType=gp3,Iops=6000,Throughput=250}' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=geth}]'

First boot takes under a minute after the instance is running: it reads the instance's VPC ranges and public address, grows the data volume's file system to the volume size, writes /root/geth-notes.txt, and starts geth, which generates its node key and JWT secret.

Step 2: Connect to your instance

Operating system SSH user
Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

The login message shows the status and console commands.

Step 3: Check the services

systemctl is-active cloudimg-geth-vpc-cidrs geth
active
active

geth-firstboot runs once: systemctl is-active geth-firstboot shows active after the first boot and inactive on every later boot, where it is skipped.

The listeners: SSH and the peer-to-peer port for everyone; the three RPC ports on the loopback address only:

sudo ss -Htuln | awk '{print $1, $5}' | sort -u
tcp *:30303
tcp 0.0.0.0:22
tcp 127.0.0.1:8545
tcp 127.0.0.1:8546
tcp 127.0.0.1:8551
tcp 127.0.0.53%lo:53
tcp 127.0.0.54:53
tcp [::]:22
udp *:30303
udp 127.0.0.1:323
udp 127.0.0.53%lo:53
udp 127.0.0.54:53
udp <private-ip>%ens5:68
udp [::1]:323

(127.0.0.53 and 127.0.0.54 are Ubuntu's local DNS resolver and port 68 and 323 are DHCP and the time service.)

Step 4: The node's status

sudo geth-status
network: mainnet   advertised: extip:203.0.113.10
{
 "enode": "enode://6a017d2a...9464acd9@203.0.113.10:30303",
 "peers": 6,
 "syncing": {
  "currentBlock": 0,
  "highestBlock": 0,
  "startingBlock": 0,
  ...
 },
 "head": 0
}

(The syncing object has more counters than shown here; the enode is shortened.)

advertised is the public address geth tells other nodes, read from the metadata service at this boot (it follows a new address after a stop and start). The enode is this instance's public peer-to-peer identity; it is not a secret. syncing and head stay at the genesis block until a consensus client drives geth (Step 7).

The notes first boot wrote (no secret is in them):

sudo cat /root/geth-notes.txt

Step 5: The geth console

geth-attach opens the geth JavaScript console over the IPC socket, as the geth account, in a sandbox that cannot open network connections. With --exec it runs one expression:

sudo geth-attach --exec 'net.peerCount'
6

(0 in the first minute after the first boot; it rises as geth finds peers.)

sudo geth-attach --exec 'eth.chainId()'
"0x1"

Run sudo geth-attach with no argument for the interactive console (type exit to leave). The IPC console has every namespace, including admin, so keep it to administrators of the instance.

Step 6: Use the RPC from your computer

On the instance, the JSON-RPC answers on the loopback address:

curl -s -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' http://127.0.0.1:8545
{"jsonrpc":"2.0","id":1,"result":"0x1"}

From your own computer, forward the port over SSH and point your tools at http://127.0.0.1:8545 (and ws://127.0.0.1:8546 for WebSocket):

ssh -i <your-key.pem> -N -L 8545:127.0.0.1:8545 -L 8546:127.0.0.1:8546 ubuntu@<public-ip>

From outside the instance the RPC ports are closed. Checked from another host against two fresh instances, with a temporary security group rule allowing 8545 to 8551 from that host only (so that the security group did not hide the answer), geth refused all three:

8545 refused
8546 refused
8551 refused

If you need RPC for other machines, put an authenticating proxy or a private load balancer in front of it and keep 8545 and 8546 off the internet: an open Ethereum RPC is scanned and abused within minutes.

Step 7: Pair a consensus client

geth follows the chain only when a consensus client sends it the chain head over the engine API. Until then eth.syncing stays at block 0, and once something has called the engine API (the self-test in Step 11 does) the journal repeats Beacon client online, but no consensus updates received in a while. Please fix your beacon client to follow the chain!. That warning is expected until your consensus client runs. Install a consensus client of your choice (Lighthouse, Prysm, Teku, Nimbus or Lodestar) on this instance, following its own documentation, and configure it with:

  • Execution endpoint: http://127.0.0.1:8551 (the engine API listens on the loopback address only).
  • JWT secret: /var/lib/geth/jwt.hex, which geth wrote at its first start. It is readable by the geth account only. Give the consensus client's account its own copy, for example sudo install -m 0400 -o <beacon-account> /var/lib/geth/jwt.hex /etc/<beacon>/jwt.hex, and never copy it off the instance.
  • Checkpoint sync from a trusted beacon state provider, so the beacon node syncs in minutes instead of days.
  • Network: the same network as geth (mainnet by default, see Step 9).

The consensus client needs its own inbound peer port in the security group (usually 9000 TCP and UDP) and its own disk space. Once it runs, geth logs the forkchoice updates it receives and sudo geth-status shows syncing progress and then the head block. Snap sync of mainnet takes many hours, depending on disk throughput.

Step 8: Storage for the chain

The chain data, the node key and the JWT secret live on the data volume at /var/lib/geth:

df -h /var/lib/geth

To enlarge it later, increase the size of the volume in the EC2 console (Volumes, Modify volume) or with aws ec2 modify-volume, wait until the modification is in the optimizing or completed state, then grow the file system online:

sudo systemctl restart cloudimg-geth-growfs
df -h /var/lib/geth

The instance also grows the file system at every boot. Use gp3 with enough IOPS and throughput (6000 IOPS and 250 MB/s or more for mainnet), and back the volume up with EBS snapshots.

Step 9: The tunables

geth reads four settings from /etc/default/geth:

grep -v '^#' /etc/default/geth
GETH_NETWORK=mainnet
GETH_SYNCMODE=snap
GETH_CACHE=1024
GETH_MAXPEERS=50
  • GETH_NETWORK: mainnet, sepolia or hoodi. Change the network only on an empty data volume: stop geth, empty /var/lib/geth (this deletes the chain data and the node identity), set the network, start geth.
  • GETH_SYNCMODE: snap (default) or full.
  • GETH_CACHE: memory for geth's caches in MB (512 to 65536). Raise it on larger instances, for example 4096 on a 16 GiB instance.
  • GETH_MAXPEERS: 1 to 200.

For example, to lower the peer limit to 25 (less upload traffic, see Prerequisites):

sudo sed -i 's/^GETH_MAXPEERS=.*/GETH_MAXPEERS=25/' /etc/default/geth
sudo systemctl restart geth

Edit the file with sudo and restart geth with sudo systemctl restart geth after every change. The guard accepts only these four keys, each once, in the plain KEY=value form; anything else keeps geth stopped and the reason is in journalctl -u geth. Other geth flags are not configurable through this file on purpose: a systemd drop-in that changes the command line is checked by the same guard, which refuses any flag that would expose the RPC or unlock an account.

Step 10: The VPC ranges and a fail-closed start

At every boot cloudimg-geth-vpc-cidrs reads this instance's VPC IPv4 and IPv6 ranges from the metadata service (IMDSv2) and adds them to geth's outbound block. geth starts only after that:

systemctl show -p ActiveState --value cloudimg-geth-vpc-cidrs
active

If the metadata service does not answer at boot, the unit retries on its own, and geth stays stopped instead of starting without the block; it starts by itself once the ranges are read. On a later boot the last ranges read successfully on this instance are used if the metadata service is unavailable. If you switch the metadata service off, geth does not start: switch it back on (IMDSv2 only is fine).

Step 11: Check the whole instance

sudo geth-selftest
GETH_SELFTEST_OK geth 1.17.10 mainnet chainId 1: user geth (pid 1118); listeners loopback 8545/8546/8551 + 30303 tcp/udp; no personal/admin/debug/miner/txpool over HTTP; Host + WebSocket Origin pinned; engine API JWT-only; IPC console as geth (network-denied): peers 0, syncing yes

The self-test checks the process and its account, the version, the listeners, the chain id, the RPC namespaces, the Host and Origin checks, the engine API's JWT authentication (with this instance's secret, which it never prints) and the IPC console.

Step 12: Versions, licences and source

cat /usr/share/doc/geth/README.cloudimg
geth 1.17.10 1df30b18d9ed35ee9d28ffd4648442c2204554ad
release geth-linux-amd64-1.17.10-1df30b18.tar.gz sha256 e016aca8cc391c8d5d14dc17c9af589488d99c37ed71d892213c80757935ba64
source go-ethereum-v1.17.10.zip h1:b3WO+xux3pItpm13kqT5Xddv01zOxBFZxQgk/Ate4/M=
modules 129 (third-party/MODULES.txt)
guide https://www.cloudimg.co.uk/guides/go-ethereum-geth-1-17-10-on-ubuntu-24-04-aws/

geth is free software: the geth command is under the GNU General Public License version 3 or later and the go-ethereum library under the GNU Lesser General Public License version 3 or later. The binary is the go-ethereum project's own release build, and it links many Go modules statically; /usr/share/doc/geth/NOTICE lists them and their licences, and /usr/share/doc/geth/third-party/ holds every module's licence and notice files. The complete go-ethereum source at the release tag is on the instance in /usr/src/geth, and /usr/share/doc/geth/WRITTEN-OFFER explains how to get the corresponding source of everything else.

Ubuntu's unattended upgrades keep the operating system patched. geth does not update itself: the cloudimg listing adds a new version when the go-ethereum project releases one, and upgrading a running node is a stop, a binary swap and a start on the same data volume. Watch the go-ethereum releases for hard forks: every node must run a release that supports the next network upgrade before it activates.

go-ethereum is developed by the go-ethereum project and its contributors. cloudimg packages and configures it and is not affiliated with or endorsed by the go-ethereum project or the Ethereum Foundation. Running a node does not need, and this image does not hold, any wallet or funds.

Support

cloudimg provides support for this image by email at support@cloudimg.co.uk, 24 hours a day, 7 days a week. Tell us the AMI ID and Region, and include the output of sudo geth-selftest and systemctl status geth cloudimg-geth-vpc-cidrs. For questions about geth itself, see the go-ethereum documentation.