Skip to main content

Overview

With accounts bootstrapped and integrations configured, you’re ready to deploy the core AWS infrastructure. This includes networking (VPC, subnets, NAT), EKS clusters, and application-specific resources like S3 buckets and IAM roles.

Decisions

Before deploying, review and customize these configuration options in each stack’s config.tm.hcl:

Networking

SettingLocationDefaultDescription
VPC CIDRglobals.networking.vpc_cidr10.0.0.0/16IP address range for the VPC. Use non-overlapping ranges if deploying multiple VPCs or connecting to on-premise networks.
NAT modeglobals.networking.nat_modefck_natHow private subnets access the internet. See NAT Gateway Modes below.
Bastion hostglobals.networking.enable_bastiontrueWhether to create a bastion host for SSH/SSM access to private resources.
PlanetScale endpointglobals.networking.planetscale_endpoint_service_nameRegion-specificVPC endpoint for PlanetScale private connectivity. Remove if not using PlanetScale.

EKS Cluster

SettingLocationDefaultDescription
Kubernetes versionglobals.eks.kubernetes_version1.34EKS control plane version. Update when upgrading clusters.
Node group versionglobals.eks.base_node_group_kubernetes_version1.34Can lag control plane during rolling upgrades.
Public endpointglobals.eks.endpoint_public_accessfalseWhether the API server is publicly accessible. Set false for private-only access (requires bastion/VPN).
Private endpointglobals.eks.endpoint_private_accesstrueWhether the API server is accessible from within the VPC.
ArgoCD hostnameglobals.eks.argocd_hostnameEnvironment-specificFQDN for ArgoCD (used for webhook configuration).

Hardcoded Values You May Want to Customize

These values are set in the Terraform modules and require editing the module source code to change:
SettingLocationDefaultDescription
Availability zonesterraform/modules/*/main.tfFirst 3 AZsNumber of AZs used for subnets. Currently hardcoded to 3.
Base node group sizingterraform/modules/eks/main.tf2-3 nodesmin_size, max_size, desired_size for the managed node group.
Base node group instance typesterraform/modules/eks/variables.tft3.largeInstance types for the initial managed node group.
Base node group AMIterraform/modules/eks/variables.tfAL2023_x86_64_STANDARDAMI type (AL2023, Bottlerocket, etc.).
fck-nat instance typeterraform/modules/networking/variables.tft4g.nanoInstance size for fck-nat NAT instances.
Bastion instance typeterraform/modules/networking/variables.tft4g.nanoInstance size for the bastion host.
EKS addon versionsterraform/modules/eks/variables.tfPinned versionsVersions for CoreDNS, VPC CNI, kube-proxy, EBS CSI driver, Pod Identity agent.
SSO admin role ARNterraform/modules/eks/variables.tfHardcodedIAM Identity Center role granted cluster admin access.

Infrastructure Deployment Order

Terramate manages dependencies between stacks automatically. The deployment order is:
1. Networking     → VPC, subnets, NAT gateway, bastion host
2. EKS            → Kubernetes cluster, node groups, Karpenter
3. App Resources  → S3 buckets, IAM roles for workloads
Each stack declares its dependencies, so Terramate applies them in the correct order.

Initial Deployment (Local)

For the first deployment, you must run Terraform locally. The CI/CD workflow only applies changed stacks, and new stacks without any Terraform state aren’t detected as changed.
Terramate can deploy all stacks at once with automatic dependency ordering (terramate run --tags staging -- terraform apply). However, deploying stacks sequentially makes it easier to follow progress, verify each component is working, and troubleshoot issues.
1

Authenticate to AWS

Use Leapp to start a session for the Infrastructure account:
leapp session start "Infrastructure"
# Verify you're authenticated
aws sts get-caller-identity
2

Initialize and deploy networking

cd terraform
export REGION="us-east-2"  # Your AWS region
export STAGE="staging"     # or "prod"

# Initialize the networking stack
terramate run --tags ${STAGE}:${REGION}:networking -- terraform init

# Preview changes
terramate run --tags ${STAGE}:${REGION}:networking -- terraform plan

# Apply
terramate run --tags ${STAGE}:${REGION}:networking -- terraform apply
3

Deploy EKS

After networking is complete:
# Initialize the EKS stack
terramate run --tags ${STAGE}:${REGION}:eks -- terraform init

# Preview and apply
terramate run --tags ${STAGE}:${REGION}:eks -- terraform plan
terramate run --tags ${STAGE}:${REGION}:eks -- terraform apply
EKS cluster creation takes 10-15 minutes.
4

Deploy example application resources (optional)

If you have application-specific infrastructure:
terramate run --tags ${STAGE}:${REGION}:services -- terraform init
terramate run --tags ${STAGE}:${REGION}:services -- terraform apply
5

Commit the lock files

After successful deployment, commit any updated .terraform.lock.hcl files:
git add terraform/live/**/.terraform.lock.hcl
git commit -m "chore: update terraform lock files after initial deployment"
git push

Subsequent Changes via Pull Request

After the initial deployment, use pull requests for all infrastructure changes. This ensures changes are reviewed and tracked.
1

Make your changes

Edit the relevant config.tm.hcl or module files, then regenerate:
cd terraform
terramate generate
2

Create a branch and push

git checkout -b infra/update-eks-version
git add .
git commit -m "chore: upgrade EKS to 1.34"
git push -u origin infra/update-eks-version
3

Open a PR and review the plan

The CI workflow will:
  1. Run terraform plan for each changed stack
  2. Post plan output to Terramate Cloud (if configured)
  3. Post a plan summary as a PR comment (if using Terramate Cloud)
Review the plan carefully before approving.
4

Merge to apply

Once approved, merge the PR. The deploy workflow will apply changes in dependency order.

What Gets Created

Networking Stack

ResourceDescription
VPCIsolated network with configurable CIDR (default: 10.0.0.0/16)
Public subnets3 subnets across availability zones for load balancers
Private subnets3 subnets for EKS nodes and workloads + bastion
NAT gatewayInternet access for private subnets (fck-nat by default for cost savings)
S3 VPC endpointFree gateway endpoint for S3 access without NAT
Bastion hostEC2 instance for SSH tunneling to private resources

EKS Stack

ResourceDescription
EKS clusterManaged Kubernetes control plane
Managed node groupInitial nodes for system workloads
Karpenter PrerequisitesAutoscaler for dynamic node provisioning
EBS CSI driverPersistent volume support with encryption
Pod IdentityAWS IAM integration for workload authentication
CoreDNS, kube-proxyEssential cluster add-ons

Application Resources (go-backend example)

ResourceDescription
S3 bucketApplication-specific storage
IAM rolePod Identity role for AWS API access
Pod Identity associationLinks the IAM role to Kubernetes ServiceAccount

Configuration Options

NAT Gateway Modes

The networking module supports three NAT modes via nat_mode variable:
ModeCostAvailabilityUse Case
fck_nat~$5/month per AZHA with auto-failoverDevelopment, staging
single_nat_gateway~$45/monthSingle point of failureCost-sensitive production
one_nat_gateway_per_az~$135/monthFull HAProduction with strict availability requirements
Configure in terraform/live/staging/<REGION>/networking/config.tm.hcl:
globals {
  nat_mode = "fck_nat"  # or "single_nat_gateway" or "one_nat_gateway_per_az"
}

EKS Node Configuration

Karpenter handles most node provisioning, but you can configure the initial managed node group:
globals {
  eks_managed_node_groups = {
    system = {
      instance_types = ["m6i.large"]
      min_size       = 2
      max_size       = 4
      desired_size   = 2
    }
  }
}

Verify Deployment

After deployment completes:
1

Check Terraform outputs

terramate run --tags ${STAGE}:${REGION}:eks -- terraform output
Note the cluster_name output; you’ll need it for later steps.
2

Verify cluster health in AWS Console

Navigate to the EKS console and verify:
  • Cluster status is Active
  • Node group shows nodes in Ready state
  • Add-ons (CoreDNS, kube-proxy, VPC CNI, EBS CSI) are Active
If you configured private-only endpoint access (endpoint_public_access = false), you cannot run kubectl commands from your local machine without first connecting through the bastion host. Console verification is sufficient for now; you’ll configure cluster access via ArgoCD in the next step.

Deploy Production

Production deployment follows the same pattern with production-specific configuration:
# List production stacks
terramate list --tags prod:infrastructure

# Deploy locally (replace with your region)
terramate run --tags prod:${REGION}:networking -- terraform apply
terramate run --tags prod:${REGION}:eks -- terraform apply

Next Steps

With infrastructure deployed, proceed to Cluster Access to configure kubectl access to your EKS cluster.