Getting Started
Date: 2026-06-21
Purpose
Section titled “Purpose”This guide deploys BetterNAT into a disposable AWS VPC, verifies private-subnet egress, and destroys all resources.
Use this first. Do not start by replacing a production NAT Gateway.
This guide is for the current Terraform install path.
Important:
- BetterNAT does not publish a public BetterNAT AMI yet.
- Terraform launches an explicit Linux AMI and uses cloud-init to install release artifacts at boot.
- The example uses one AZ.
- The example uses small EC2 instances and tiny HTTP probes.
- It does not run expensive multi-TB traffic tests.
What This Test Proves
Section titled “What This Test Proves”This disposable test answers the first operational questions:
- Terraform can install the BetterNAT provider.
- BetterNAT gateway nodes can bootstrap from release artifacts.
- The active node can own the private route target.
- A private test client can reach the public internet.
betternat status,doctor --live, and metrics expose useful state.- Terraform destroy can clean up the test stack.
Flow Diagram
Section titled “Flow Diagram”Before:
After:
For the datapath component BetterNAT uses inside each node, see the upstream LoxiLB overview image and LoxiLB standalone documentation. BetterNAT uses LoxiLB as a local egress SNAT datapath; AWS route/EIP failover is handled by betternat-agent.
Prerequisites
Section titled “Prerequisites”Install locally:
- Terraform,
- AWS CLI,
jq,- an AWS profile with permission to create EC2, Auto Scaling, IAM, DynamoDB, and SSM resources.
Choose:
export AWS_PROFILE="<your-profile>"export AWS_REGION="us-west-2"export BETTERNAT_AZ="us-west-2a"export BETTERNAT_VERSION="v0.2.0"export BETTERNAT_RUN_ID="betternat-test-$(date -u +%Y%m%d%H%M%S)"Expected AWS costs:
- EC2 gateway instances,
- one private test EC2 client,
- EBS volumes,
- EIP when stable egress IP is enabled,
- DynamoDB lease table,
- normal public internet data transfer,
- CloudWatch/SSM/logging if enabled by your account defaults.
Select Runtime Version
Section titled “Select Runtime Version”Set betternat_version on the betternat_aws_gateway resource. The provider uses
that version plus instance_type to select the correct Linux release artifacts
and built-in SHA256 checksums for bootstrap.
For unreleased local builds, use the maintainer AWS supplemental runbook instead
of this user quick start. That runbook may override agent_binary_url,
agent_binary_sha256, cli_binary_url, and cli_binary_sha256 for test-only
binaries.
Install Provider
Section titled “Install Provider”The public Quick Start pins the current provider version:
source = "nowakeai/betternat"version = "= 0.2.0"Terraform Registry install is the default path:
terraform -chdir=examples/terraform-aws-supplemental initIf Registry availability is temporarily delayed, install the provider from the GitHub release as a filesystem mirror:
source scripts/setup-provider-github-mirror.shWhen using the mirror fallback, keep the TF_CLI_CONFIG_FILE environment
variable exported in the same shell for terraform init, terraform plan,
terraform apply, and terraform destroy.
Deploy Disposable VPC
Section titled “Deploy Disposable VPC”Initialize if needed:
terraform -chdir=examples/terraform-aws-supplemental initApply:
terraform -chdir=examples/terraform-aws-supplemental apply \ -var "run_id=$BETTERNAT_RUN_ID" \ -var "region=$AWS_REGION" \ -var "az=$BETTERNAT_AZ" \ -var "betternat_version=$BETTERNAT_VERSION"Expected:
- isolated VPC,
- public and private subnet,
- two BetterNAT gateway nodes in an ASG,
- one private test client,
- DynamoDB lease table,
- route table ownership moved to the active node,
- EIP associated to the active node when
stable_egress_ip=true.
Verify
Section titled “Verify”Get outputs:
terraform -chdir=examples/terraform-aws-supplemental outputGet the active gateway and private client instance IDs:
export BETTERNAT_ACTIVE_INSTANCE_ID="$( terraform -chdir=examples/terraform-aws-supplemental output -json active_instance_ids | jq -r 'to_entries[0].value')"
export BETTERNAT_PRIVATE_CLIENT_ID="$( terraform -chdir=examples/terraform-aws-supplemental output -raw private_client_instance_id)"Use SSM to run the gateway checks:
export BETTERNAT_GATEWAY_CHECK_COMMAND_ID="$( aws ssm send-command \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --instance-ids "$BETTERNAT_ACTIVE_INSTANCE_ID" \ --document-name "AWS-RunShellScript" \ --parameters '{"commands":["betternat version","betternat-agent --version","systemctl is-active betternat-agent.service","sudo betternat status","sudo betternat doctor --live"]}' \ --query "Command.CommandId" \ --output text)"
aws ssm wait command-executed \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --command-id "$BETTERNAT_GATEWAY_CHECK_COMMAND_ID" \ --instance-id "$BETTERNAT_ACTIVE_INSTANCE_ID"
aws ssm list-command-invocations \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --command-id "$BETTERNAT_GATEWAY_CHECK_COMMAND_ID" \ --details \ --query "CommandInvocations[].CommandPlugins[].Output" \ --output textFrom the private test client, verify public egress:
export BETTERNAT_CLIENT_CHECK_COMMAND_ID="$( aws ssm send-command \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --instance-ids "$BETTERNAT_PRIVATE_CLIENT_ID" \ --document-name "AWS-RunShellScript" \ --parameters '{"commands":["curl -fsS https://checkip.amazonaws.com","curl -fsSI https://example.com"]}' \ --query "Command.CommandId" \ --output text)"
aws ssm wait command-executed \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --command-id "$BETTERNAT_CLIENT_CHECK_COMMAND_ID" \ --instance-id "$BETTERNAT_PRIVATE_CLIENT_ID"
aws ssm list-command-invocations \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --command-id "$BETTERNAT_CLIENT_CHECK_COMMAND_ID" \ --details \ --query "CommandInvocations[].CommandPlugins[].Output" \ --output textFor stable EIP mode, the source IP should match the BetterNAT EIP.
Destroy
Section titled “Destroy”Destroy Terraform resources:
terraform -chdir=examples/terraform-aws-supplemental destroy \ -var "run_id=$BETTERNAT_RUN_ID" \ -var "region=$AWS_REGION" \ -var "az=$BETTERNAT_AZ" \ -var "betternat_version=$BETTERNAT_VERSION"Residual scan:
aws resourcegroupstaggingapi get-resources \ --profile "$AWS_PROFILE" \ --region "$AWS_REGION" \ --tag-filters "Key=BetterNATRunId,Values=$BETTERNAT_RUN_ID"Terminated EC2 instances can remain visible briefly in tag results. Confirm direct EC2 state before treating them as live resources.
Next Steps
Section titled “Next Steps”- Read Operations Guide to understand day-2 status, metrics, handover records, and cleanup checks.
- Read EKS Terraform Module Integration if you need to adapt an existing modular Terraform/EKS repository.
- Read Existing VPC Install before touching real private route tables.