Ethereum node setup AWS is not a 30-minute tutorial. By mid-2025, Ethereum full node data surpassed 3TB and grows at approximately 14GB per week. The post-Merge architecture requires running two clients simultaneously an execution layer client and a consensus layer client with each having different resource profiles, different sync mechanisms, and different failure modes.
Getting the ethereum node setup AWS wrong means either running out of disk space in six months, underprovisioning IOPS and watching the node fall behind during peak load, or exposing RPC endpoints that get hammered by bots the moment they hit the public internet.
This guide covers the complete production ethereum node setup AWS: EC2 instance selection, EBS storage configuration with correct IOPS provisioning, VPC and security group design, client installation and configuration, checkpoint sync, Terraform infrastructure-as-code, and the monitoring stack that tells you about problems before they affect your applications.
Understanding the Ethereum Node Architecture in 2026
Before selecting a single AWS resource, understand what you are running. Post-Merge Ethereum requires two clients running in tandem:
Execution Layer (EL) client: handles transaction processing, state management, and the JSON-RPC API that your applications call. Options: Geth, Nethermind, Erigon, Besu.
Consensus Layer (CL) client: handles the proof-of-stake consensus, validator duties, and the Beacon API. Options: Lighthouse, Prysm, Teku, Nimbus.
The two clients communicate via an authenticated Engine API using a shared JWT secret. If either client goes down, the node stops serving data. If the JWT secret is misconfigured, the clients cannot communicate and the node fails to sync.
Client selection for your ethereum node setup AWS:
For most production deployments on AWS, the most operationally solid combination is Geth (execution) and Lighthouse (consensus). Geth has the most community support and documentation. Lighthouse is written in Rust, is memory-efficient, and has maintained strong uptime across multiple network upgrades.
If storage cost is the primary concern, Nethermind with Lighthouse is worth evaluating Nethermind’s full node footprint is approximately 1TB vs Geth’s 1.3-2TB in snap-sync mode, though Geth’s new path-based archive mode (v1.16+) now achieves approximately 2TB for full history.
Client diversity matters for the network as a whole. If Geth or Lighthouse represents more than 33% of the network, a bug in that client could affect finality. Check the current client distribution at clientdiversity.org and consider Besu or Nethermind for execution if Geth is dominant.
Nethermind’s full node footprint is approximately 1TB vs Geth’s 1.3-2TB in snap-sync mode, though Geth’s new path-based archive mode (v1.16+) now achieves approximately 2TB for full history.
Client diversity matters for the network as a whole. If Geth or Lighthouse represents more than 33% of the network, a bug in that client could affect finality. Check the current client distribution at clientdiversity.org and consider Besu or Nethermind for execution if Geth is dominant.
Step 1: AWS Infrastructure Design
A production ethereum node setup AWS requires three decisions before writing any configuration: compute, storage, and network isolation.
The recommended architecture for a production ethereum node setup AWS:
A sync node handles the initial chain sync and maintains the canonical state. It periodically snapshots its EBS volume to S3. When you need to scale or recover from a failure, new RPC nodes bootstrap from the S3 snapshot instead of syncing from scratch reducing recovery time from days to hours.
RPC nodes sit behind an Application Load Balancer, serving your applications. They auto-scale based on request volume.
This architecture matches what AWS itself recommends in their official Ethereum on AWS guidance and eliminates the single point of failure inherent in a single-node deployment.
Step 2: EC2 Instance Selection
The instance type decision for an ethereum node setup AWS is primarily a RAM and EBS bandwidth decision. Ethereum clients are memory-intensive, and disk I/O is the most common bottleneck during sync and under load.
Recommended instance types in 2026:
For a sync node (initial sync and state management):
r6g.2xlarge– 8 vCPU, 64GB RAM, Graviton2 (ARM) – best price-to-RAM ratio.r6i.2xlarge– 8 vCPU, 64GB RAM, Intel – if you need x86 for client compatibility.- EBS bandwidth: up to 10Gbps on both.
For RPC nodes (serving application traffic):
r6i.4xlarge– 16 vCPU, 128GB RAM – for high-throughput RPC with many concurrent connections.m6i.2xlarge– 8 vCPU, 32GB RAM – for moderate traffic with cost sensitivity.
Why RAM matters this much: During sync, Geth and Nethermind load large portions of the state trie into memory. Under-provisioned RAM causes excessive disk reads, which dramatically slows sync and degrades RPC response times under load. The 64GB recommendation is not conservative, it is the practical minimum for stable full-node operation at mainnet scale in 2026.
Graviton considerations: AWS Graviton2/3 instances offer 20-40% better price-to-performance for most Ethereum workloads. All major clients compile and run correctly on ARM64. If you are starting fresh, Graviton is the right choice.
Step 3: EBS Storage Configuration
Storage is where most ethereum node setup AWS deployments get it wrong. The two failure modes are: insufficient capacity (running out of space mid-operation) and insufficient IOPS (node falls behind chain head under load).
Storage requirements in 2026:
- Full node (Geth snap-sync): 2TB minimum, 3TB recommended for 12+ months of headroom.
- Full node (Nethermind): 1.5TB minimum, 2.5TB recommended.
- Archive node (Geth): 15TB+ and growing approximately 60GB/week.
- Archive node (Erigon): approximately 2.5TB significantly more efficient than Geth for archive.
EBS volume configuration:
# Terraform - EBS volume for Ethereum node
resource "aws_ebs_volume" "ethereum_data" {
availability_zone = "eu-west-1a"
size = 3000 # 3TB for full node with headroom
type = "gp3"
iops = 16000 # Maximum gp3 IOPS - critical for sync performance
throughput = 1000 # Maximum gp3 throughput in MB/s
encrypted = true
kms_key_id = aws_kms_key.ethereum.arn
tags = {
Name = "ethereum-node-data"
Environment = var.environment
ManagedBy = "Terraform"
}
}Why gp3 with maximum IOPS provisioning: gp3 decouples IOPS and throughput from volume size, which means you can provision 16,000 IOPS on a 3TB volume for approximately $0.008/provisioned IOPS/month, significantly cheaper than io2 for the same performance. AWS EBS gp3 now supports up to 80,000 IOPS as of late 2025, but provisioning 16,000 is sufficient for full-node workloads.
The eth-docker community notes that AWS EBS gp3 with provisioned IOPS delivers acceptable but not optimal performance for sync-intensive phases if you are finding sync is stalling, the issue is almost always insufficient IOPS provisioning on the EBS volume, not the EC2 instance.
Separate root and data volumes: Always use a separate EBS volume for chain data. This allows you to snapshot and restore chain data independently of the operating system, resize the data volume without instance replacement, and avoid running out of root volume space.
Step 4: VPC and Security Group Design
The security mistakes in a typical ethereum node setup AWS are predictable: RPC exposed to the internet, SSH on port 22 with password auth enabled, no VPC isolation. Here is the correct design:
# Terraform - Security Group for Ethereum node
resource "aws_security_group" "ethereum_node" {
name = "${var.environment}-ethereum-node"
description = "Ethereum full node security group"
vpc_id = var.vpc_id
# Execution layer P2P (Geth)
ingress {
from_port = 30303
to_port = 30303
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Geth P2P TCP"
}
ingress {
from_port = 30303
to_port = 30303
protocol = "udp"
cidr_blocks = ["0.0.0.0/0"]
description = "Geth P2P UDP discovery"
}
# Consensus layer P2P (Lighthouse)
ingress {
from_port = 9000
to_port = 9000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Lighthouse P2P TCP"
}
ingress {
from_port = 9000
to_port = 9000
protocol = "udp"
cidr_blocks = ["0.0.0.0/0"]
description = "Lighthouse P2P UDP"
}
# JSON-RPC - internal VPC only, never public internet
ingress {
from_port = 8545
to_port = 8545
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
description = "Geth JSON-RPC - VPC internal only"
}
# Beacon API - internal VPC only
ingress {
from_port = 5052
to_port = 5052
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
description = "Lighthouse Beacon API - VPC internal only"
}
# Monitoring - internal VPC only
ingress {
from_port = 9090
to_port = 9090
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
description = "Prometheus metrics - VPC internal only"
}
# SSH via SSM - no port 22 needed
# AWS Systems Manager Session Manager handles shell access
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "All outbound"
}
}Port 8545 (JSON-RPC) is the most common misconfiguration. An open JSON-RPC endpoint gets discovered by bots within minutes and will be used for free RPC calls. Always restrict it to your VPC CIDR or to specific application security groups.
The absence of port 22 is intentional: AWS Systems Manager Session Manager provides authenticated shell access without exposing SSH to any network. See our Terraform AWS validator guide for the IAM configuration that enables SSM.
Step 5: EC2 Instance and IAM Configuration
# Terraform - EC2 instance for Ethereum node
resource "aws_instance" "ethereum_node" {
ami = data.aws_ami.ubuntu.id
instance_type = "r6g.2xlarge"
subnet_id = var.private_subnet_id
iam_instance_profile = aws_iam_instance_profile.ethereum.name
vpc_security_group_ids = [aws_security_group.ethereum_node.id]
# Root volume - OS only
root_block_device {
volume_type = "gp3"
volume_size = 50
encrypted = true
delete_on_termination = true
}
# IMDSv2 only - prevent SSRF-based metadata theft
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
# No public IP - node lives in private subnet
associate_public_ip_address = false
user_data = base64encode(templatefile("${path.module}/templates/user_data.sh", {
environment = var.environment
aws_region = var.aws_region
}))
tags = {
Name = "${var.environment}-ethereum-node"
Environment = var.environment
ManagedBy = "Terraform"
}
}
# IAM role - minimal permissions
resource "aws_iam_role" "ethereum" {
name = "${var.environment}-ethereum-node-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
}]
})
}
# SSM access for shell without SSH
resource "aws_iam_role_policy_attachment" "ssm" {
role = aws_iam_role.ethereum.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# CloudWatch for metrics
resource "aws_iam_role_policy_attachment" "cloudwatch" {
role = aws_iam_role.ethereum.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
# S3 for snapshots - scoped to ethereum bucket only
resource "aws_iam_role_policy" "s3_snapshots" {
name = "ethereum-s3-snapshots"
role = aws_iam_role.ethereum.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["s3:GetObject", "s3:PutObject", "s3:ListBucket"]
Resource = ["arn:aws:s3:::${var.snapshot_bucket}", "arn:aws:s3:::${var.snapshot_bucket}/*"]
}]
})
}Step 6: Client Installation and Configuration
With the infrastructure in place, install and configure the clients:
#!/bin/bash
# user_data.sh - runs on first boot
# System preparation
apt-get update && apt-get upgrade -y
apt-get install -y curl jq wget unzip
# Mount the data EBS volume
mkfs.ext4 /dev/nvme1n1
mkdir -p /data
echo '/dev/nvme1n1 /data ext4 defaults,nofail 0 2' >> /etc/fstab
mount -a
# Create dedicated user
useradd ethereum --create-home --shell /bin/bash
# Install Geth (execution client)
add-apt-repository -y ppa:ethereum/ethereum
apt-get update
apt-get install -y ethereum
# Install Lighthouse (consensus client)
LIGHTHOUSE_VERSION="v5.3.0"
wget "https://github.com/sigp/lighthouse/releases/download/${LIGHTHOUSE_VERSION}/lighthouse-${LIGHTHOUSE_VERSION}-aarch64-unknown-linux-gnu.tar.gz"
tar xzf lighthouse-*.tar.gz
mv lighthouse /usr/local/bin/
chmod +x /usr/local/bin/lighthouse
lighthouse --version
# Generate JWT secret for EL/CL authentication
mkdir -p /data/jwt
openssl rand -hex 32 > /data/jwt/jwtsecret
chmod 640 /data/jwt/jwtsecret
chown ethereum:ethereum /data/jwt/jwtsecretGeth systemd service:
# /etc/systemd/system/geth.service
[Unit]
Description=Geth Execution Client
After=network-online.target
Wants=network-online.target
[Service]
User=ethereum
ExecStart=/usr/bin/geth \
--datadir /data/geth \
--authrpc.jwtsecret /data/jwt/jwtsecret \
--authrpc.addr 0.0.0.0 \
--authrpc.port 8551 \
--http \
--http.addr 0.0.0.0 \
--http.port 8545 \
--http.vhosts "*" \
--http.api eth,net,web3,txpool \
--ws \
--ws.addr 0.0.0.0 \
--ws.port 8546 \
--metrics \
--metrics.addr 0.0.0.0 \
--metrics.port 6060 \
--cache 4096 \
--maxpeers 50 \
--syncmode snap
Restart=always
RestartSec=5
LimitNOFILE=65535
TimeoutStopSec=300
[Install]
WantedBy=multi-user.targetLighthouse systemd service with checkpoint sync:
# /etc/systemd/system/lighthouse.service
[Unit]
Description=Lighthouse Consensus Client
After=geth.service
Requires=geth.service
[Service]
User=ethereum
ExecStart=/usr/local/bin/lighthouse bn \
--datadir /data/lighthouse \
--network mainnet \
--execution-endpoint http://localhost:8551 \
--execution-jwt /data/jwt/jwtsecret \
--checkpoint-sync-url https://mainnet.checkpoint.sigp.io \
--http \
--http-address 0.0.0.0 \
--http-port 5052 \
--metrics \
--metrics-address 0.0.0.0 \
--metrics-port 5054 \
--port 9000 \
--target-peers 80
Restart=always
RestartSec=5
LimitNOFILE=65535
[Install]
WantedBy=multi-user.targetThe --checkpoint-sync-url parameter is critical for your ethereum node setup on AWS. Checkpoint sync allows Lighthouse to sync from the latest finalized checkpoint, rather than from genesis, completing in minutes rather than days. The execution layer still needs to perform snap sync, but the consensus layer is immediately operational.
Step 7: Verifying the Ethereum Node Setup AWS
After starting both clients, verify they are communicating correctly:
# Check Geth sync status
curl -s -X POST localhost:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' | jq .
# Expected output when fully synced:
# {"jsonrpc":"2.0","id":1,"result":false}
# Check peer count
curl -s -X POST localhost:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' | jq -r '.result' | \
xargs printf "Peers: %d\n"
# Check Lighthouse sync status
curl -s http://localhost:5052/eth/v1/node/syncing | jq .
# Verify EL/CL communication - Lighthouse should show execution_optimistic: false
curl -s http://localhost:5052/eth/v1/node/health
# Check current block number matches Etherscan
curl -s -X POST localhost:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | \
jq -r '.result' | xargs printf "Current block: %d\n"Your ethereum node setup on AWS is synced and healthy when eth_syncing returns false, peer count is 20+, and the block number matches Etherscan within a few blocks.
Step 8: Monitoring and Alerting
A completed ethereum node setup AWS without monitoring is incomplete. The node will drift, fall behind, or run out of disk space and you need to know before your applications are affected.
# prometheus-alerts.yaml - Ethereum node alerts
groups:
- name: ethereum_node
rules:
- alert: EthereumNodeNotSyncing
expr: |
ethereum_blockchain_height - ethereum_blockchain_height offset 5m == 0
for: 10m
labels:
severity: critical
annotations:
summary: "Ethereum node has stopped syncing for 10 minutes"
- alert: EthereumLowPeerCount
expr: p2p_peers < 10
for: 5m
labels:
severity: warning
annotations:
summary: "Low peer count - node may be isolated"
- alert: LighthouseNotFinalized
expr: |
lighthouse_beacon_head_slot - lighthouse_beacon_finalized_slot > 100
for: 10m
labels:
severity: critical
annotations:
summary: "Beacon chain not finalizing - check consensus client"
- alert: EthereumDiskSpaceCritical
expr: |
(node_filesystem_avail_bytes{mountpoint="/data"} /
node_filesystem_size_bytes{mountpoint="/data"}) * 100 < 20
for: 5m
labels:
severity: critical
annotations:
summary: "Ethereum data volume below 20% free - expand EBS immediately"
- alert: GethNotResponding
expr: up{job="geth"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Geth execution client not responding"
- alert: LighthouseNotResponding
expr: up{job="lighthouse"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Lighthouse consensus client not responding"The disk space alert is the most practically important for your ethereum node setup on AWS. Ethereum data grows approximately 14GB/week for Geth snap-sync. At 20% remaining on a 3TB volume, you have approximately 600GB and 6-7 weeks to expand before the node halts.
EBS volume expansion is online and non-disruptive on AWS, you can expand without stopping the node. But you need the monitoring in place to know when to act.
Snapshot Strategy for Fast Recovery
The final component of a production ethereum node setup on AWS is a snapshot strategy. If a node fails or you need to scale, bootstrapping from S3 snapshot is orders of magnitude faster than syncing from scratch.
#!/bin/bash
# snapshot-to-s3.sh - run via cron or Lambda on a schedule
BUCKET="your-ethereum-snapshots"
TIMESTAMP=$(date +%Y%m%d-%H%M)
DATA_VOLUME="/dev/nvme1n1"
# Stop clients gracefully
systemctl stop lighthouse
systemctl stop geth
# Create EBS snapshot
SNAPSHOT_ID=$(aws ec2 create-snapshot \
--volume-id "${EBS_VOLUME_ID}" \
--description "ethereum-node-${TIMESTAMP}" \
--query 'SnapshotId' \
--output text)
echo "Snapshot created: ${SNAPSHOT_ID}"
# Tag snapshot for lifecycle management
aws ec2 create-tags \
--resources "${SNAPSHOT_ID}" \
--tags "Key=Environment,Value=production" "Key=Type,Value=ethereum-node" "Key=Timestamp,Value=${TIMESTAMP}"
# Restart clients
systemctl start geth
sleep 30
systemctl start lighthouse
echo "Snapshot complete - clients restarted"Conclusion
A production ethereum node setup AWS requires deliberate decisions at every layer: memory-optimised instances for the RAM requirements, gp3 EBS with maximum IOPS provisioned for disk throughput, VPC isolation with RPC never exposed to the internet, checkpoint sync for fast consensus client bootstrapping, and monitoring that catches disk space and peer count issues before they affect applications.
The teams that run stable Ethereum nodes in production are not the ones who followed a quick-start guide. They are the ones who understood the storage growth curve, provisioned IOPS correctly from day one, and have runbooks for every failure mode before that failure happens.
At The Good Shell, we design and deploy Ethereum node infrastructure for Web3 teams from single nodes for development to multi-node high-availability RPC infrastructure for DeFi protocols. See our Web3 infrastructure services or read our case studies to see what production Ethereum infrastructure looks like.
For the official AWS guidance on running Ethereum nodes, the AWS Blockchain Node Runners blueprint for Ethereum is the authoritative reference for AWS-specific patterns.

