Create an AWS Hub for Centralized Egress
Use this guide to provision the customer-side infrastructure required for centralized egress on a Redpanda Bring Your Own Cloud (BYOC) cluster on AWS. After completing the procedure, you have a hub VPC with a NAT Gateway, a Transit Gateway shared with the Redpanda cluster account, and the IDs needed to configure your BYOC network. You can then follow Configure Centralized Egress with AWS Transit Gateway to enable centralized egress at cluster creation.
After reading this page, you will be able to:
-
Provision a hub VPC with a NAT Gateway and Transit Gateway on AWS
-
Share the Transit Gateway with the Redpanda cluster account using AWS RAM
-
Collect the Transit Gateway ID and NAT Gateway public IP needed for BYOC cluster configuration
What this sets up
A hub VPC with a single NAT Gateway provides centralized internet egress for one or more spoke VPCs. All outbound traffic from spokes is routed through the hub and exits from one public IP, which you can use for outbound IP allowlisting on external services.
Spoke VPC --> Transit Gateway --> Hub Private Subnet --> NAT Gateway --> Internet Gateway --> Internet
^
|
Return traffic via Transit Gateway ---+
Traffic path:
-
The spoke private subnet sends
0.0.0.0/0to the Transit Gateway. -
The Transit Gateway default route forwards
0.0.0.0/0to the hub VPC attachment. -
The hub private subnet routes
0.0.0.0/0to the NAT Gateway. -
The NAT Gateway exits through the Internet Gateway.
-
Return traffic enters through the Internet Gateway. The hub public route table sends it back to the Transit Gateway, which delivers it to the correct spoke.
Automated alternative
If you prefer to provision this infrastructure with Terraform, the configuration used to produce the setup in this guide is published in the Redpanda Cloud Examples repository: aws-hub-egress
The rest of this guide covers the equivalent steps using the AWS Console or AWS CLI.
Prerequisites
-
AWS CLI configured with credentials for the hub account.
-
Permissions on the hub account for VPC, EC2, Transit Gateway, AWS RAM, and IAM operations.
-
The Redpanda spoke VPC CIDR must not overlap your hub VPC CIDR.
-
If the Redpanda cluster runs in a different AWS account than your hub, note the Redpanda cluster account ID for the AWS RAM share.
|
Hub and spoke VPC CIDRs must not overlap. AWS allows Transit Gateway attachments with overlapping CIDRs, but the hub VPC’s local route always wins over the spoke CIDR return route, which silently blackholes reply traffic. Plan your CIDRs before you start. For background, see What are CIDRs? |
Default values
The procedure uses the values in the following table. Replace any value that does not match your environment.
| Parameter | Default | Notes |
|---|---|---|
Hub VPC CIDR |
|
CGNAT range, unlikely to conflict with typical workload CIDRs |
Public subnet 1 |
|
AZ index 0 |
Public subnet 2 |
|
AZ index 1 |
Private subnet 1 |
|
AZ index 0 |
Private subnet 2 |
|
AZ index 1 |
Spoke CIDR |
|
Replace with your actual Redpanda spoke VPC CIDR |
Region |
|
Replace with your target region |
Create the hub VPC
-
Console
-
CLI
-
Open the VPC console and choose Your VPCs > Create VPC.
-
Set the name to
hub-vpc. -
Set the IPv4 CIDR to
100.64.0.0/16. -
Enable DNS resolution and DNS hostnames.
-
Click Create VPC.
VPC_ID=$(aws ec2 create-vpc \
--cidr-block 100.64.0.0/16 \
--region us-east-2 \
--query 'Vpc.VpcId' --output text)
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-support
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames
aws ec2 create-tags --resources $VPC_ID \
--tags Key=Name,Value=hub-vpc \
--region us-east-2
echo "VPC ID: $VPC_ID"
Get availability zones
The public and private subnets each require two availability zones.
AZ1=$(aws ec2 describe-availability-zones \
--region us-east-2 \
--filters Name=opt-in-status,Values=opt-in-not-required \
--query 'AvailabilityZones[0].ZoneName' --output text)
AZ2=$(aws ec2 describe-availability-zones \
--region us-east-2 \
--filters Name=opt-in-status,Values=opt-in-not-required \
--query 'AvailabilityZones[1].ZoneName' --output text)
echo "AZ1: $AZ1, AZ2: $AZ2"
Create public subnets
Leave auto-assign public IPv4 disabled. The hub VPC never assigns public IPs to instances directly.
-
Console
-
CLI
-
Open the VPC console and choose Subnets > Create Subnet.
-
Select the
hub-vpcVPC. -
Create two public subnets:
-
Name
hub-public-0, in the first AZ, with CIDR100.64.0.0/24. -
Name
hub-public-1, in the second AZ, with CIDR100.64.1.0/24.
-
-
Leave Auto-assign public IPv4 disabled.
PUB_SUBNET_0=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 100.64.0.0/24 \
--availability-zone $AZ1 \
--region us-east-2 \
--query 'Subnet.SubnetId' --output text)
PUB_SUBNET_1=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 100.64.1.0/24 \
--availability-zone $AZ2 \
--region us-east-2 \
--query 'Subnet.SubnetId' --output text)
aws ec2 create-tags --resources $PUB_SUBNET_0 \
--tags Key=Name,Value=hub-public-0 --region us-east-2
aws ec2 create-tags --resources $PUB_SUBNET_1 \
--tags Key=Name,Value=hub-public-1 --region us-east-2
echo "Public subnets: $PUB_SUBNET_0, $PUB_SUBNET_1"
Create private subnets
The private subnets are where the Transit Gateway lands traffic delivered from the Redpanda spoke. They have no Internet Gateway route, so workloads in them never get public IPs, and outbound traffic exits the hub through the NAT Gateway.
-
Console
-
CLI
-
Open the VPC console and choose Subnets > Create Subnet.
-
Select the
hub-vpcVPC. -
Create two private subnets:
-
Name
hub-private-0, in the first AZ, with CIDR100.64.2.0/24. -
Name
hub-private-1, in the second AZ, with CIDR100.64.3.0/24.
-
PRIV_SUBNET_0=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 100.64.2.0/24 \
--availability-zone $AZ1 \
--region us-east-2 \
--query 'Subnet.SubnetId' --output text)
PRIV_SUBNET_1=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 100.64.3.0/24 \
--availability-zone $AZ2 \
--region us-east-2 \
--query 'Subnet.SubnetId' --output text)
aws ec2 create-tags --resources $PRIV_SUBNET_0 \
--tags Key=Name,Value=hub-private-0 --region us-east-2
aws ec2 create-tags --resources $PRIV_SUBNET_1 \
--tags Key=Name,Value=hub-private-1 --region us-east-2
echo "Private subnets: $PRIV_SUBNET_0, $PRIV_SUBNET_1"
Create the Internet Gateway
The Internet Gateway gives the hub VPC its outbound path to the internet. The NAT Gateway depends on it for source-NAT to a public IP, and reply traffic from the internet enters the hub through it.
-
Console
-
CLI
-
Open the VPC console and choose Internet Gateways > Create internet gateway.
-
Set the name to
hub-igw. -
Click Create.
-
Choose Actions > Attach to VPC and select
hub-vpc.
IGW_ID=$(aws ec2 create-internet-gateway \
--region us-east-2 \
--query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway \
--internet-gateway-id $IGW_ID \
--vpc-id $VPC_ID \
--region us-east-2
aws ec2 create-tags --resources $IGW_ID \
--tags Key=Name,Value=hub-igw --region us-east-2
echo "IGW ID: $IGW_ID"
Create the NAT Gateway
The NAT Gateway goes in the first public subnet only. It requires an Elastic IP.
-
Console
-
CLI
-
Open the VPC console and choose Elastic IPs > Allocate Elastic IP address > Allocate.
-
Choose NAT Gateways > Create NAT gateway.
-
Set the name to
hub-nat. -
Select subnet
hub-public-0. -
Set Connectivity type to Public.
-
Select the Elastic IP you just allocated.
-
Click Create NAT Gateway. The NAT Gateway takes a few minutes to become available.
EIP_ALLOC=$(aws ec2 allocate-address \
--domain vpc \
--region us-east-2 \
--query 'AllocationId' --output text)
NAT_GW_ID=$(aws ec2 create-nat-gateway \
--subnet-id $PUB_SUBNET_0 \
--allocation-id $EIP_ALLOC \
--region us-east-2 \
--query 'NatGateway.NatGatewayId' --output text)
aws ec2 create-tags --resources $NAT_GW_ID \
--tags Key=Name,Value=hub-nat --region us-east-2
aws ec2 wait nat-gateway-available \
--nat-gateway-ids $NAT_GW_ID \
--region us-east-2
echo "NAT Gateway ID: $NAT_GW_ID"
Create the Transit Gateway
-
Console
-
CLI
-
Open the VPC console and choose Transit Gateways > Create transit gateway.
-
Set the name to
hub-tgw. -
Enable Default route table association, Default route table propagation, and Auto accept shared attachments.
-
Click Create transit gateway. The Transit Gateway takes a few minutes to become available.
TGW_ID=$(aws ec2 create-transit-gateway \
--description "Hub egress transit gateway" \
--options "DefaultRouteTableAssociation=enable,DefaultRouteTablePropagation=enable,AutoAcceptSharedAttachments=enable" \
--region us-east-2 \
--query 'TransitGateway.TransitGatewayId' --output text)
aws ec2 create-tags --resources $TGW_ID \
--tags Key=Name,Value=hub-tgw --region us-east-2
aws ec2 wait transit-gateway-available \
--transit-gateway-ids $TGW_ID \
--region us-east-2
HUB_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
TGW_ARN="arn:aws:ec2:us-east-2:${HUB_ACCOUNT}:transit-gateway/$TGW_ID"
echo "Transit Gateway ID: $TGW_ID"
echo "Transit Gateway ARN: $TGW_ARN"
Attach the hub VPC to the Transit Gateway
The hub VPC attaches using its private subnets. This is where the Transit Gateway delivers inbound traffic into the hub.
-
Console
-
CLI
-
Open the VPC console and choose Transit Gateway Attachments > Create transit gateway attachment.
-
Select the
hub-tgwTransit Gateway. -
Set Attachment type to VPC.
-
Select the
hub-vpcVPC. -
Select both private subnets (
hub-private-0andhub-private-1). -
Enable Associate with the default association route table and Propagate to the default propagation route table.
-
Click Create. The attachment takes a few minutes to become available.
TGW_ATTACH_ID=$(aws ec2 create-transit-gateway-vpc-attachment \
--transit-gateway-id $TGW_ID \
--vpc-id $VPC_ID \
--subnet-ids $PRIV_SUBNET_0 $PRIV_SUBNET_1 \
--options "ApplianceModeSupport=disable,DnsSupport=enable,Ipv6Support=disable" \
--region us-east-2 \
--query 'TransitGatewayVpcAttachment.TransitGatewayAttachmentId' --output text)
aws ec2 create-tags --resources $TGW_ATTACH_ID \
--tags Key=Name,Value=hub-tgw-attachment --region us-east-2
aws ec2 wait transit-gateway-attachment-available \
--transit-gateway-attachment-ids $TGW_ATTACH_ID \
--region us-east-2
echo "Transit Gateway Attachment ID: $TGW_ATTACH_ID"
Add a default route to the Transit Gateway route table
This static route forwards all 0.0.0.0/0 traffic to the hub VPC attachment, so spoke traffic reaches the NAT Gateway.
-
Console
-
CLI
-
Open the VPC console and choose Transit Gateway Route Tables.
-
Select the default route table for
hub-tgw. -
Choose Routes > Create static route.
-
Set the CIDR to
0.0.0.0/0. -
Select the hub VPC attachment.
-
Click Create static route.
TGW_RTB_ID=$(aws ec2 describe-transit-gateway-route-tables \
--filters "Name=transit-gateway-id,Values=$TGW_ID" \
"Name=default-association-route-table,Values=true" \
--region us-east-2 \
--query 'TransitGatewayRouteTables[0].TransitGatewayRouteTableId' --output text)
aws ec2 create-transit-gateway-route \
--destination-cidr-block 0.0.0.0/0 \
--transit-gateway-route-table-id $TGW_RTB_ID \
--transit-gateway-attachment-id $TGW_ATTACH_ID \
--region us-east-2
echo "Default route added to Transit Gateway route table: $TGW_RTB_ID"
Create the route tables
Public route table
The public subnet route table needs two types of routes:
-
0.0.0.0/0to the Internet Gateway — allows the NAT Gateway to reach the internet. -
One entry per spoke CIDR to the Transit Gateway — sends reply packets back to the correct spoke.
-
Console
-
CLI
-
Open the VPC console and choose Route Tables > Create route table.
-
Name it
hub-public-rtand select thehub-vpcVPC. -
Click Create.
-
Choose Routes > Edit routes and add the following:
-
Destination
0.0.0.0/0, target type Internet Gateway, valuehub-igw. -
Destination
10.0.0.0/16(your spoke CIDR), target type Transit Gateway, valuehub-tgw. Repeat this entry for each additional spoke.
-
-
Choose Subnet Associations > Edit subnet associations and select both public subnets.
PUB_RTB_ID=$(aws ec2 create-route-table \
--vpc-id $VPC_ID \
--region us-east-2 \
--query 'RouteTable.RouteTableId' --output text)
aws ec2 create-tags --resources $PUB_RTB_ID \
--tags Key=Name,Value=hub-public-rt --region us-east-2
aws ec2 create-route \
--route-table-id $PUB_RTB_ID \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id $IGW_ID \
--region us-east-2
# Add one route per spoke CIDR. Repeat this block for each additional spoke.
aws ec2 create-route \
--route-table-id $PUB_RTB_ID \
--destination-cidr-block 10.0.0.0/16 \
--transit-gateway-id $TGW_ID \
--region us-east-2
aws ec2 associate-route-table \
--route-table-id $PUB_RTB_ID \
--subnet-id $PUB_SUBNET_0 \
--region us-east-2
aws ec2 associate-route-table \
--route-table-id $PUB_RTB_ID \
--subnet-id $PUB_SUBNET_1 \
--region us-east-2
Private route table
Private subnets only need a default route to the NAT Gateway. Traffic arrives from the Transit Gateway and exits using NAT.
-
Console
-
CLI
-
Open the VPC console and choose Route Tables > Create route table.
-
Name it
hub-private-rtand select thehub-vpcVPC. -
Click Create.
-
Choose Routes > Edit routes and add
0.0.0.0/0with target type NAT Gateway and valuehub-nat. -
Choose Subnet Associations > Edit subnet associations and select both private subnets.
PRIV_RTB_ID=$(aws ec2 create-route-table \
--vpc-id $VPC_ID \
--region us-east-2 \
--query 'RouteTable.RouteTableId' --output text)
aws ec2 create-tags --resources $PRIV_RTB_ID \
--tags Key=Name,Value=hub-private-rt --region us-east-2
aws ec2 create-route \
--route-table-id $PRIV_RTB_ID \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id $NAT_GW_ID \
--region us-east-2
aws ec2 associate-route-table \
--route-table-id $PRIV_RTB_ID \
--subnet-id $PRIV_SUBNET_0 \
--region us-east-2
aws ec2 associate-route-table \
--route-table-id $PRIV_RTB_ID \
--subnet-id $PRIV_SUBNET_1 \
--region us-east-2
Share the Transit Gateway with the Redpanda account
Skip this section if the Redpanda BYOC cluster runs in the same AWS account as your hub.
Use AWS Resource Access Manager (RAM) to share the Transit Gateway with the Redpanda cluster account.
-
Console
-
CLI
-
In the hub account, open the AWS RAM console and choose Resource shares > Create resource share.
-
Set the name to
hub-tgw-share. -
Under Resources, select resource type Transit Gateways and pick
hub-tgw. -
Under Principals, enter the Redpanda cluster AWS account ID.
-
Click Create resource share.
-
If the accounts are not in the same AWS Organization, switch to the BYOC AWS account, open AWS RAM, and accept the pending invitation under Resource share invitations. Accept the invitation before you create the Redpanda BYOC cluster. Within an AWS Organization, the share is auto-accepted.
In the hub account:
REDPANDA_ACCOUNT_ID="<redpanda-account-id>"
RAM_SHARE_ARN=$(aws ram create-resource-share \
--name hub-tgw-share \
--resource-arns "$TGW_ARN" \
--principals "$REDPANDA_ACCOUNT_ID" \
--region us-east-2 \
--query 'resourceShare.resourceShareArn' --output text)
echo "RAM Share ARN: $RAM_SHARE_ARN"
If the accounts are not in the same AWS Organization, the spoke side must accept the invitation. Within an AWS Organization, shares are auto-accepted.
Collect the values to provide to Redpanda
Record these values. Provide the Transit Gateway ID when you configure centralized egress on your BYOC network. See Configure Centralized Egress with AWS Transit Gateway.
NAT_PUBLIC_IP=$(aws ec2 describe-nat-gateways \
--nat-gateway-ids $NAT_GW_ID \
--region us-east-2 \
--query 'NatGateways[0].NatGatewayAddresses[0].PublicIp' --output text)
echo "hub_vpc_id = $VPC_ID"
echo "transit_gateway_id = $TGW_ID"
echo "transit_gateway_arn = $TGW_ARN"
echo "nat_gateway_public_ip = $NAT_PUBLIC_IP"
echo "ram_resource_share_arn = $RAM_SHARE_ARN"
All outbound internet traffic from every BYOC spoke that attaches to this Transit Gateway exits from the same NAT Gateway public IP. Use that public IP for outbound IP allowlisting on external services.
Plan for high availability
The procedure in this guide deploys a single NAT Gateway in hub-public-0, which is not AZ-resilient. For production deployments, run one NAT Gateway per availability zone and use per-AZ private route tables that direct local traffic to the AZ-local NAT Gateway. The Terraform reference module also deploys a single NAT Gateway. Extend it with per-AZ NAT Gateways if you need HA egress.
Troubleshooting
| Symptom | Likely cause |
|---|---|
Spoke cannot reach the internet |
Missing |
Outbound traffic leaves the hub NAT Gateway but no replies arrive at the spoke |
Missing spoke CIDR route in the hub public route table. |
Transit Gateway attachment is stuck in |
The BYOC AWS account has not accepted the AWS RAM share invitation. In that account, open AWS RAM and accept the pending invitation. Shares are auto-accepted only within an AWS Organization. |
CIDR conflict error on attachment |
The spoke CIDR overlaps the hub CIDR |
Outbound traffic is intermittently dropped |
Your hub firewall is not allowlisting the third-party endpoints required by Redpanda. See Internet endpoints required from your hub. |
All traffic appears to come from one public IP |
This is the expected behavior of centralized egress. Use that IP for outbound allowlisting. |