Provision AWS Infrastructure
This provisions the cloud infrastructure Confident AI runs on: an EKS cluster, an RDS PostgreSQL database, S3 buckets, and the keyless IAM wiring (EKS Pod Identity). When it finishes you will have a running cluster and a set of outputs to feed into the Helm chart on the next page.
The region comes from your AWS provider and profile, there is no region variable. Set the region and two availability zones once:
export AWS_REGION=us-east-1
export AWS_AZ_1=${AWS_REGION}a
export AWS_AZ_2=${AWS_REGION}bCreate the network (optional)
This builds a VPC with two public and two private subnets across two availability zones, plus a NAT gateway. EKS and RDS run in the private subnets; the public subnets carry the NAT gateway and any internet-facing load balancer. Run the blocks in order, each one feeds IDs into the next.
Create the VPC and turn on DNS so the cluster and database can resolve private hostnames:
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.20.0.0/16 \ --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=confident-prod-vpc}]' \ --query Vpc.VpcId --output text) aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-supportAttach an internet gateway. The public subnets route to it for internet access:
INTERNET_GATEWAY_ID=$(aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output text) aws ec2 attach-internet-gateway --internet-gateway-id $INTERNET_GATEWAY_ID --vpc-id $VPC_IDCreate two public and two private subnets, one of each per availability zone:
PUBLIC_SUBNET_1=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.0.0/20 --availability-zone $AWS_AZ_1 --query Subnet.SubnetId --output text) PUBLIC_SUBNET_2=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.16.0/20 --availability-zone $AWS_AZ_2 --query Subnet.SubnetId --output text) PRIVATE_SUBNET_1=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.128.0/20 --availability-zone $AWS_AZ_1 --query Subnet.SubnetId --output text) PRIVATE_SUBNET_2=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.144.0/20 --availability-zone $AWS_AZ_2 --query Subnet.SubnetId --output text)Tag the subnets so a load balancer controller can discover them later:
aws ec2 create-tags --resources $PUBLIC_SUBNET_1 $PUBLIC_SUBNET_2 --tags Key=kubernetes.io/role/elb,Value=1 aws ec2 create-tags --resources $PRIVATE_SUBNET_1 $PRIVATE_SUBNET_2 --tags Key=kubernetes.io/role/internal-elb,Value=1Create a NAT gateway in the first public subnet. The private nodes reach the internet through it for outbound pulls, with no inbound exposure:
NAT_EIP_ALLOCATION_ID=$(aws ec2 allocate-address --domain vpc --query AllocationId --output text) NAT_GATEWAY_ID=$(aws ec2 create-nat-gateway --subnet-id $PUBLIC_SUBNET_1 --allocation-id $NAT_EIP_ALLOCATION_ID --query NatGateway.NatGatewayId --output text) aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_GATEWAY_IDRoute the public subnets to the internet gateway:
PUBLIC_ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id $VPC_ID --query RouteTable.RouteTableId --output text) aws ec2 create-route --route-table-id $PUBLIC_ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $INTERNET_GATEWAY_ID aws ec2 associate-route-table --route-table-id $PUBLIC_ROUTE_TABLE_ID --subnet-id $PUBLIC_SUBNET_1 aws ec2 associate-route-table --route-table-id $PUBLIC_ROUTE_TABLE_ID --subnet-id $PUBLIC_SUBNET_2Route the private subnets through the NAT gateway:
PRIVATE_ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id $VPC_ID --query RouteTable.RouteTableId --output text) aws ec2 create-route --route-table-id $PRIVATE_ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_GATEWAY_ID aws ec2 associate-route-table --route-table-id $PRIVATE_ROUTE_TABLE_ID --subnet-id $PRIVATE_SUBNET_1 aws ec2 associate-route-table --route-table-id $PRIVATE_ROUTE_TABLE_ID --subnet-id $PRIVATE_SUBNET_2Print the VPC and private subnet IDs to paste into the Terraform config:
echo "confident_vpc_id = \"$VPC_ID\"" echo "confident_private_subnet_ids = [\"$PRIVATE_SUBNET_1\", \"$PRIVATE_SUBNET_2\"]"Mirror the code sandbox image (required)
Code-based and transformer metrics run in a sandboxed Lambda, which runs the
confident-code-sandbox-awsimage. Lambda only runs container images from an ECR in the same account, so mirror the public image into your own ECR.Create the ECR repository and log Docker in to it:
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) ECR_REGISTRY=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com aws ecr create-repository --repository-name confident-code-sandbox-aws --region $AWS_REGION aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REGISTRYPull the public image and push it to your ECR. The last line prints the image URI for the Terraform config:
docker pull confidentai/confident-code-sandbox-aws:latest docker tag confidentai/confident-code-sandbox-aws:latest $ECR_REGISTRY/confident-code-sandbox-aws:latest docker push $ECR_REGISTRY/confident-code-sandbox-aws:latest echo "confident_code_executor_lambda_image_uri = \"$ECR_REGISTRY/confident-code-sandbox-aws:latest\""Write the Terraform config
Create a
main.tfthat references the published module, then fill in the values from the previous two steps. What each variable does:- Network:
confident_vpc_idandconfident_private_subnet_idscome from the network step (or use your own VPC). - Naming:
confident_environmentandconfident_environment_codestamp theprodnaming convention onto every resource. - Access:
confident_public_eksexposes the cluster API to your machine; set itfalsefor a private-only endpoint. - Durability:
confident_rds_deletion_protectionguards the database against accidental deletion. - Managed services:
confident_create_secrets_managerandconfident_managed_redis_enabledturn on the recommended secret store and Redis, andconfident_database_ingress_cidrslets the nodes reach Redis (use your VPC CIDR). - Code executor:
confident_code_executor_lambda_image_uriis the ECR image URI printed by the mirror step.
provider "aws" { region = "us-east-1" } module "confident_ai" { source = "confident-ai/confident-ai/aws" version = "~> 0.1" confident_vpc_id = "vpc-xxxxxxxx" confident_private_subnet_ids = ["subnet-aaaa", "subnet-bbbb"] confident_environment = "prod" confident_environment_code = "p" confident_public_eks = true confident_rds_deletion_protection = true confident_create_secrets_manager = true confident_managed_redis_enabled = true confident_database_ingress_cidrs = ["10.20.0.0/16"] confident_code_executor_enabled = true confident_code_executor_lambda_image_uri = "<ECR image URI from the mirror step>" } output "helm_values" { value = module.confident_ai.helm_values sensitive = true }- Network:
Configure remote state (optional)
terraform { backend "s3" { bucket = "confident-tfstate" key = "confident-ai/aws/terraform.tfstate" region = "us-east-1" } }Apply
terraform init terraform plan terraform applyCreating the EKS cluster and RDS takes roughly 15 to 20 minutes.
Connect to the cluster
eval "$(terraform output -raw configure_kubectl)" kubectl get nodesEKS ships no default storage class, and ClickHouse and Redis need disks. Create a gp3 default once:
kubectl apply -f - <<'EOF' apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp3 annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: ebs.csi.aws.com volumeBindingMode: WaitForFirstConsumer parameters: type: gp3 encrypted: "true" allowVolumeExpansion: true EOFRead the outputs
The Helm chart on the next page needs these values.
terraform output helm_valuesprints a ready-to-paste snippet, or read them individually. The comment on each line is the Helm value it feeds:terraform output -raw database_url # secrets.data.DATABASE_URL terraform output test_cases_bucket # storage.testCasesBucket terraform output payloads_bucket # storage.payloadsBucket terraform output -raw region # storage.aws.region terraform output code_executor_function_name # codeExecutor.aws.lambdaFunctionNameBecause the app uses EKS Pod Identity, its ServiceAccount needs no annotation, Terraform already linked the IAM role to it.
Managed secrets and Redis (recommended)
The module block above provisions both. The Deploy page installs the External Secrets Operator and wires them into the chart:
- Secrets Manager + External Secrets Operator (
confident_create_secrets_manager = true): Terraform creates an empty secret and grants ESO read access through Pod Identity. - ElastiCache for Redis (
confident_managed_redis_enabled = true): managed Redis instead of the in-cluster one.confident_database_ingress_cidrsopens port 6379 to your VPC so the nodes can reach it.terraform output -raw redis_urlgives the value forredis.externalUrl.
Inputs reference
The variables you are most likely to set. For the complete, always-current list, see the module inputs on the Terraform Registry.
Required
| Variable | Description |
|---|---|
confident_vpc_id | Existing VPC to deploy into. |
confident_private_subnet_ids | Two or more existing private subnets in different availability zones (EKS and RDS). |
Commonly set (optional, with production defaults)
| Variable | Default | Description |
|---|---|---|
confident_environment / confident_environment_code | stage / s | Naming convention stamped on resources (use prod / p). |
confident_public_eks | false | Expose the cluster API endpoint. |
confident_eks_admin_arn | "" | Extra IAM principal granted cluster-admin. |
confident_node_instance_types / confident_node_group_desired_size | ["m6i.2xlarge"] / 4 | Node group sizing. |
confident_code_executor_enabled / confident_code_executor_lambda_image_uri | true / "" | Code executor Lambda and its ECR image URI. |
confident_create_secrets_manager | false | Secrets Manager secret and ESO Pod Identity. |
confident_managed_redis_enabled / confident_database_ingress_cidrs | false / [] | ElastiCache, and the CIDRs allowed to reach it. |
confident_rds_deletion_protection / confident_rds_multi_az | false / true | RDS durability options. |
The region comes from your AWS provider, there is no region variable.
Next step
Deploy with Helm
Install the app with the Terraform outputs and expose it over HTTPS.
Last updated on