ACP-267: Primary Network validator uptime requirement increases from 80% to 90%.Read the proposal

Deploy an L1 with Terraform and Ansible

Launch a production-ready Avalanche L1 with validators, RPC nodes, and monitoring on AWS, GCP, or Azure using Terraform and Ansible.

This guide walks through deploying a complete Avalanche L1 blockchain on cloud VMs. By the end, you will have a running L1 with validators, archive and pruned RPC nodes, monitoring, and an eRPC load balancer.

Supported clouds: AWS (full feature set), GCP, Azure. Time to deploy: ~30 minutes (plus sync time). Cost: ~$651/month on AWS.

Architecture

Infrastructure Sizing (AWS)

ComponentInstanceDiskPurpose
Validators (default: 3, production: 5)c6a.xlarge500GB EBS gp3Block production, consensus
Archive RPCc6a.xlarge1TB EBS gp3Full history, debug/trace APIs, block explorer
Pruned RPCc6a.large500GB EBS gp3State-sync, transaction workloads
Monitoringt3.small50GB EBS gp3Prometheus, Grafana, eRPC

RPC Node Types

TypeAPIsPruningState-SyncUse Case
ArchiveFull (incl. debug_*, trace_*)DisabledDisabledBlock explorer, debugging, historical queries
PrunedStandard (eth, net, web3)EnabledEnabledTransaction submission, latest state queries

GCP and Azure use a single generic rpc pool instead of the archive/pruned split. The archive/pruned separation is AWS-only.

Step-by-Step Deployment

Configure Cloud Credentials and SSH

# AWS
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"

# Generate an SSH key for node access
ssh-keygen -t rsa -b 4096 -f ~/.ssh/avalanche-deploy -N ""

For GCP, authenticate with gcloud auth application-default login. For Azure, use az login.

Configure Terraform Variables

cd terraform/l1/aws    # or terraform/l1/gcp, terraform/l1/azure
cp terraform.tfvars.example terraform.tfvars

Edit terraform.tfvars:

name_prefix              = "my-l1"
environment              = "fuji"        # or "mainnet"
validator_count          = 5
rpc_archive_count        = 1
rpc_pruned_count         = 1
ssh_public_key           = "ssh-rsa AAAA..."
ssh_private_key_file     = "~/.ssh/avalanche-deploy"
enable_staking_key_backup = true

Provision Cloud Infrastructure

make infra    # Runs terraform init && terraform apply

This creates all VMs, networking (VPC, subnets, security groups), and storage (S3 bucket for staking keys if enabled). Terraform auto-generates the Ansible inventory at ansible/inventory/aws_hosts.

Deploy AvalancheGo

make deploy
make status    # Wait for "P:OK" on all nodes

This runs playbook l1/deploy-nodes.yml, which:

  • Installs AvalancheGo on all nodes
  • Starts syncing with the Primary Network using partial-sync-primary-network: true (syncs only P-Chain headers — much faster than a full sync)
  • Collects NodeIDs and saves them to ansible/node_ids.txt
  • Backs up initial staking keys to S3 (if configured)

Configure Your Genesis

Before creating the L1, prepare your genesis file at configs/l1/genesis/genesis.json.

Use the Genesis Builder to generate this visually, or edit the included template.

Key settings:

  • chainId — Unique EVM chain ID (check availability)
  • feeConfig — Gas limits and base fees
  • warpConfig — Cross-chain messaging with quorumNumerator: 67
  • alloc — Pre-funded addresses and pre-deployed contracts

Create Your L1

# Import or create a deployer key
platform keys import --name l1-deployer
platform keys default --name l1-deployer

# Build and run the create-l1 tool
make create-l1
./tools/create-l1/create-l1 \
  --network=fuji \
  --key-name=l1-deployer \
  --validators=$(cd terraform/l1/aws && terraform output -json validator_ips | jq -r 'join(",")') \
  --chain-name=mychain \
  --output=l1.env

The create-l1 tool executes three P-Chain transactions:

  1. CreateSubnetTx — Creates a new Subnet (returns SUBNET_ID)
  2. CreateChainTx — Creates the EVM chain with your genesis (returns CHAIN_ID)
  3. ConvertSubnetToL1Tx — Converts the Subnet to an L1, registering all validators with their BLS keys

The output l1.env file contains SUBNET_ID, CHAIN_ID, CONVERSION_TX, and EVM_CHAIN_ID.

Your deployer key must be funded on the P-Chain. On Fuji, get test AVAX from the Builder Hub Faucet and cross-chain transfer to P-Chain via Core Wallet.

Configure Nodes for Your L1

source l1.env
make configure-l1 SUBNET_ID=$SUBNET_ID CHAIN_ID=$CHAIN_ID
make status

This runs playbook l1/configure.yml, which:

  • Adds track-subnets: <SUBNET_ID> to each node's config
  • Copies the appropriate chain config (archive, pruned, or validator)
  • Restarts AvalancheGo on all nodes
  • Automatically deploys eRPC as a load balancer (auto-detects EVM chain ID from genesis)

Your L1 is now running. Access it at:

EndpointURLNotes
eRPC (recommended)http://<monitoring-ip>:4000Load balanced, cached, automatic failover
Direct Archive RPChttp://<archive-rpc-ip>:9650/ext/bc/<CHAIN_ID>/rpcFull debug/trace APIs
Direct Pruned RPChttp://<pruned-rpc-ip>:9650/ext/bc/<CHAIN_ID>/rpcStandard APIs only
Grafanahttp://<monitoring-ip>:3000Default credentials: admin/admin

Optional: Initialize ValidatorManager

If your genesis includes a pre-deployed ValidatorManager proxy contract, initialize it to enable on-chain validator management:

# Install Foundry (if not already installed)
curl -L https://foundry.paradigm.xyz | bash && foundryup

# Set ICM contracts path
export ICM_CONTRACTS_PATH=~/code/icm-contracts

# Initialize
source l1.env
make initialize-validator-manager \
  SUBNET_ID=$SUBNET_ID \
  CHAIN_ID=$CHAIN_ID \
  CONVERSION_TX=$CONVERSION_TX \
  PROXY_ADDRESS=0xfacade01... \
  EVM_CHAIN_ID=$EVM_CHAIN_ID

Cost Estimate (AWS us-east-1)

ComponentCountMonthly Estimate
Validators (c6a.xlarge)5~$450
Archive RPC (c6a.xlarge)1~$120
Pruned RPC (c6a.large)1~$65
Monitoring (t3.small)1~$15
S3 + KMS~$1
Total~$651/mo

Next Steps

Is this guide helpful?