Launch Week 02 wrapped — explore all five launches

Provision AWS Infrastructure

Included on the Enterprise plan. Book a demo, opens in a new tab. Not included on the Team plan. Not included on the Starter plan. Not included on the Free plan.

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}b
  1. Create 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-support

    Attach 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_ID

    Create 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=1

    Create 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_ID

    Route 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_2

    Route 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_2

    Print 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\"]"
  2. Mirror the code sandbox image (required)

    Code-based and transformer metrics run in a sandboxed Lambda, which runs the confident-code-sandbox-aws image. 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_REGISTRY

    Pull 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\""
  3. Write the Terraform config

    Create a main.tf that references the published module, then fill in the values from the previous two steps. What each variable does:

    • Network: confident_vpc_id and confident_private_subnet_ids come from the network step (or use your own VPC).
    • Naming: confident_environment and confident_environment_code stamp the prod naming convention onto every resource.
    • Access: confident_public_eks exposes the cluster API to your machine; set it false for a private-only endpoint.
    • Durability: confident_rds_deletion_protection guards the database against accidental deletion.
    • Managed services: confident_create_secrets_manager and confident_managed_redis_enabled turn on the recommended secret store and Redis, and confident_database_ingress_cidrs lets the nodes reach Redis (use your VPC CIDR).
    • Code executor: confident_code_executor_lambda_image_uri is 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
    }
  4. Configure remote state (optional)

    terraform {
      backend "s3" {
        bucket = "confident-tfstate"
        key    = "confident-ai/aws/terraform.tfstate"
        region = "us-east-1"
      }
    }
  5. Apply

    terraform init
    terraform plan
    terraform apply

    Creating the EKS cluster and RDS takes roughly 15 to 20 minutes.

  6. Connect to the cluster

    eval "$(terraform output -raw configure_kubectl)"
    kubectl get nodes

    EKS 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
    EOF
  7. Read the outputs

    The Helm chart on the next page needs these values. terraform output helm_values prints 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.lambdaFunctionName

    Because the app uses EKS Pod Identity, its ServiceAccount needs no annotation, Terraform already linked the IAM role to it.

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_cidrs opens port 6379 to your VPC so the nodes can reach it. terraform output -raw redis_url gives the value for redis.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

VariableDescription
confident_vpc_idExisting VPC to deploy into.
confident_private_subnet_idsTwo or more existing private subnets in different availability zones (EKS and RDS).

Commonly set (optional, with production defaults)

VariableDefaultDescription
confident_environment / confident_environment_codestage / sNaming convention stamped on resources (use prod / p).
confident_public_eksfalseExpose 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"] / 4Node group sizing.
confident_code_executor_enabled / confident_code_executor_lambda_image_uritrue / ""Code executor Lambda and its ECR image URI.
confident_create_secrets_managerfalseSecrets Manager secret and ESO Pod Identity.
confident_managed_redis_enabled / confident_database_ingress_cidrsfalse / []ElastiCache, and the CIDRs allowed to reach it.
confident_rds_deletion_protection / confident_rds_multi_azfalse / trueRDS 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.

Find your AWS deployment path in < 15 minutesGet a tailored walkthrough of the EKS architecture, deployment options, security model, and rollout path.Book a demo

Last updated on

Built byConfident AI