Geth
Overview
Go-Ethereum (Geth) is the most widely used Ethereum execution-layer client. It runs a full Ethereum node, speaks the devp2p peer-to-peer protocol, executes the EVM, and serves the Ethereum JSON-RPC API that wallets, indexers and applications rely on. Paired with a consensus client, it can also power a validator.
This SecureAnyCloud image runs Geth on Ubuntu 24.04 on Azure and is configured to snap sync Ethereum mainnet out of the box. The RPC stays private by default, and each VM generates its own node identity and engine API token on first boot.
What you get:
- The pinned official Geth 1.17.4 static binary (commit
36a7dc72), downloaded from the officialgeth.ethereum.org build store and verified against its SHA256 checksum - Geth running as a single Go binary under systemd (
geth.service) as the unprivilegedgethuser, with no Docker - The HTTP JSON-RPC (8545), the WebSocket RPC (8546) and the engine API (8551) bound to
127.0.0.1only. The exposed HTTP API is limited to theeth,net,web3namespaces, neverpersonal,adminordebug, so the node is never an open RPC - The Network Security Group opens only the peer-to-peer port 30303 (TCP and UDP), which is how the node discovers and connects to peers
- No Ethereum account or keystore, and no insecure account unlocking
- Snap sync configured against Ethereum mainnet, with a sensible cache and peer count, and
--nat extipset from the VM public IP on first boot so peering advertises the correct address - A fresh per-instance engine API JSON Web Token (
jwt.hex,openssl rand -hex 32, mode 0640) and a fresh per-instance peer-to-peer node key, both generated on first boot and documented in/root/geth-notes.txt(mode 0600, root only) - Chaindata, the node key and the JWT on a dedicated 20 GiB Azure data disk mounted at
/var/lib/geth, so chain state survives independently of the OS disk and the volume can be resized independently - A small nginx on port
:80serving only an unauthenticated/healthzendpoint (HTTP 200) for Azure Load Balancer health probes. It is not an RPC proxy or a management UI - An Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security upgrades kept enabled
- The Azure Linux Agent for cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a 24-hour response SLA
An open Ethereum JSON-RPC endpoint on the public internet is a well-known abuse and denial-of-service vector, and a theft vector if any account is unlocked. That is why this image keeps the RPC on loopback and limits the HTTP API to eth,net,web3. A node identity or engine API secret baked into an image would also be cloned onto every customer VM, so this image ships neither. Instead, the per-VM details are written to /root/geth-notes.txt on first boot.
Prerequisites
You need an active Azure subscription, an SSH public key, a VNet with a subnet in the target region, and a subscription to the Go-Ethereum (Geth) listing on Azure Marketplace.
Configure Network Security Group rules that allow TCP 22 for administration and TCP and UDP 30303 for peer-to-peer traffic. Do not open 8545, 8546 or 8551.
Plan a large data disk for a full mainnet node (see Step 9). Mainnet chaindata needs hundreds of GiB. The 20 GiB disk that ships with the image suits the image itself, a testnet, or the very start of a snap sync.
Recommended VM size: Standard_D4s_v4 (4 vCPU, 16 GB RAM) is a practical minimum for a mainnet execution node. A serious mainnet node benefits from more RAM and fast SSD storage. Testnets run comfortably on smaller sizes.
Deploy from the Azure Portal
Search the Azure Marketplace for Go-Ethereum, select the SecureAnyCloud publisher, and click Create.
Configure the Network Security Group to allow TCP 22 for administration and TCP and UDP 30303 for peering. Leave the JSON-RPC ports closed. You query the node locally on the VM over loopback, or through a private, authenticated path that you control (see Step 10).
Deploy from the Azure CLI
RG="eth-prod"; LOCATION="eastus"; VM_NAME="geth1" IMAGE_URN="sac1647283583153:geth-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:geth-ubuntu-24-04:default:latest
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$IMAGE_URN" \
--size Standard_D4s_v4 \
--admin-username azureuser \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
# Open peering (UDP+TCP 30303) and admin (TCP 22). Do NOT open the RPC ports.
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 30303 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002Peering uses UDP 30303 for discovery and TCP 30303 for connections, so open both.
Review the first-boot results
On first boot the image resolves the VM public IP and sets --nat extip so peering advertises the correct address. It generates a fresh per-instance engine API jwt.hex and asserts that the RPC is bound to loopback with the minimal API set. It then starts Geth, which generates a fresh per-instance node key, waits for the loopback JSON-RPC to answer, starts nginx, and writes /root/geth-notes.txt. This completes within about a minute.
SSH in as azureuser and read the details:
sudo cat /root/geth-notes.txtConfirm the services are running
Check that geth.service (the node) and nginx.service (the health probe) are both active. geth version confirms the pinned 1.17.4 stable binary.
geth version systemctl is-active geth.service nginx.service systemctl status geth.service --no-pagerQuery the node over the loopback JSON-RPC
The JSON-RPC is bound to 127.0.0.1 only, so query it from a shell on the VM. These methods are useful first checks:
net_versionreturns1for mainnet.eth_blockNumberreturns the current head (0x0 until sync has progressed).web3_clientVersionidentifies the Geth build.net_peerCountshows how many peers are connected.
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"net_version","params":[],"id":1}' \
http://127.0.0.1:8545
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://127.0.0.1:8545To open an interactive JavaScript console over the local IPC socket, run the command below. The console exposes the full API, including admin.
sudo -u geth /usr/local/bin/geth attach /var/lib/geth/geth.ipcWatch peer discovery and sync
Read the Geth log to see devp2p peer discovery and snap sync progress. The node finds peers on port 30303 and begins downloading state. Add -f to follow the log live.
sudo journalctl -u geth.service --no-pager -o cat | tail -20Ethereum is proof of stake, so the execution client needs a consensus client to tell it the chain head. Until you pair one (Step 8), the log notes that no consensus updates have been received. Meanwhile the node still discovers peers and serves the loopback RPC.
Verify the RPC is not network exposed
This check confirms the core security default. ss shows that 8545, 8546 and 8551 listen on 127.0.0.1 only, while 30303 is the one open port.
sudo ss -tlnp | grep -E '8545|8546|8551|30303'
sudo ss -ulnp | grep 30303From a remote host on the internet, a request to the JSON-RPC must be refused or time out. The NSG never opens the port, and Geth never binds it to a public address.
curl --max-time 8 http://<GETH_HOST>:8545
# times out / connection refusedPair a consensus client (complete the node)
Since the Merge, a complete Ethereum node is a pair: an execution client (this image, Geth) and a consensus client (Lighthouse, Prysm, Teku or Nimbus). The consensus client drives Geth through the engine API on 127.0.0.1:8551, authenticated with the per-instance JWT at /var/lib/geth/jwt.hex.
Install a consensus client on the same VM, or reach it over the private loopback, and point it at:
execution endpoint: http://127.0.0.1:8551 jwt secret file: /var/lib/geth/jwt.hexUntil a consensus client is attached, Geth peers and serves RPC but does not advance the chain head. This is expected for an execution client on its own.
Plan sync, disk sizing and testnets
The node ships in snap sync mode against Ethereum mainnet. A full mainnet node needs hundreds of GiB of fast SSD storage.
Attach a large data disk and mount it at /var/lib/geth before the sync fills the shipped 20 GiB volume:
# attach and mount a larger disk at /var/lib/geth, then move existing chaindata onto it
sudo systemctl stop geth
# ... format and mount your new disk at /var/lib/geth, preserving ownership geth:geth ...
sudo systemctl start gethTo use a testnet instead of mainnet, set GETH_NETWORK in /etc/default/geth. Use a fresh, empty data disk so you never mix chaindata across networks.
sudo sed -i 's/^GETH_NETWORK=.*/GETH_NETWORK=--sepolia/' /etc/default/geth
sudo systemctl restart gethYou can also tune the cache and peer count in /etc/default/geth with GETH_CACHE and GETH_MAXPEERS.
Expose the RPC safely to your own applications
The RPC is loopback-only by design. When your own application tier needs to reach it, do so without binding Geth to a public address. Two safe patterns:
SSH tunnel from a trusted admin host: run ssh -L 8545:127.0.0.1:8545 azureuser@<GETH_HOST>, then query http://127.0.0.1:8545 locally.
Private VNet with an authenticated, TLS-terminating reverse proxy in front of the RPC. Make it reachable only from your application subnet, never from the public internet.
Never set --http.addr 0.0.0.0 on a public VM, and never expose the personal, admin or debug namespaces over the network.
Security recommendations
Restrict the NSG. Open only TCP and UDP 30303 for peering and TCP 22 for administration. Keep 8545, 8546 and 8551 closed.
Keep the RPC on loopback. If you must expose it, use an SSH tunnel or a private, authenticated reverse proxy (Step 10), never a public bind.
Protect the engine JWT. /var/lib/geth/jwt.hex is the shared secret between Geth and your consensus client. It is unique to each VM and mode 0640. Keep it that way.
Do not create unlocked accounts. For signing, use a dedicated remote signer or a hardware wallet, never --allow-insecure-unlock on a networked node.
Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
Back up /var/lib/geth (the data disk). You can resync chaindata, but the node key and JWT are unique to each instance.
Support and Licensing
Go-Ethereum (Geth) is developed by the go-ethereum project. It is distributed under the GNU Lesser General Public License v3.0 (LGPL-3.0) for the library and the GNU General Public License v3.0 (GPL-3.0) for the command-line binaries. This image bundles the unmodified official Geth binary.
SecureAnyCloud provides the packaging, the loopback-only RPC hardening, the per-instance node identity and JWT automation, the data disk layout, and 24/7 support with a 24-hour response SLA.
SecureAnyCloud is not affiliated with or endorsed by the go-ethereum project or the Ethereum Foundation. Geth, Go-Ethereum and Ethereum are marks of their respective owners and are used here only to identify the software.
Deploy on Azure
Find Go-Ethereum (Geth) on Ubuntu 24.04 LTS in the Azure Marketplace, published by SecureAnyCloud. Deploy from the Portal or the Azure CLI as shown above.
Need Help?
Email sales@secureanycloud.com for deployment help, configuration questions or licensing enquiries.
Deploy on Azure
Launch Go-Ethereum (Geth) 1.17 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com