Skip to main content

Architecture

The diagram below shows the architecture of the TrueFoundry compute plane.

Access Policies

The compute plane requires the following IAM policies to operate. These are created automatically during platform deployment.
EncryptionPolicy to create and manage key for encryption:

Requirements

Common requirements for setting up the compute plane:
  • Billing and STS must be enabled for the AWS account.
  • Egress access to container registries — public.ecr.aws, quay.io, ghcr.io, tfy.jfrog.io, docker.io/natsio, nvcr.io, registry.k8s.io — to pull images for ArgoCD, NATS, GPU Operator, Argo Rollouts, Argo Workflows, Istio, and Keda.
  • A domain mapped to the service endpoints, plus a certificate to encrypt traffic. A wildcard domain such as *.services.example.com is preferred. TrueFoundry supports path-based routing (e.g. services.example.com/tfy/*), but many frontend applications do not. Create the certificate for your domain in AWS Certificate Manager (ACM) and keep the ARN ready (covered in the Get Domain and Certificate ARN step below).
  • Sufficient CPU/GPU/Inferentia quotas for your use case. Check and increase them at AWS EC2 service quotas.
  • Sufficient permissions on the AWS account to create the compute-plane resources. See Permissions required to create the infrastructure for details.

Permissions required to create the infrastructure

Admin permission on the AWS account is the simplest. For the exact least-privilege set, the policy below is the base cluster policy plus the compute-plane (blob storage) delta. Substitute $REGION, $ACCOUNT_ID, and $CLUSTER_NAME with your values before attaching.
Regarding the VPC and EKS cluster, you can decide between the following scenarios:
  1. The new VPC will have a CIDR range of /20 or larger, at least 2 availability zones, and private subnets with CIDR range of /24 or larger. This ensures capacity for ~250 instances and 4096 pods.
  2. If you are using custom networking, you need a CGNAT IP address in each AZ. CGNAT space and route tables will be set up in the VPC.
  3. A NAT gateway will be provisioned to provide internet access to the private subnets.

Setting up compute plane

TrueFoundry compute plane infrastructure is provisioned using OpenTofu/Terraform. You can download the OpenTofu/Terraform code for your exact account by filling up your account details and downloading a script that can be executed on your local machine.
1

Enable Deployment Feature in the Platform (Optional)

To enable the deployment feature which allows you to deploy services through the platform, you need to enable it;
  • In the left hand navigation, go to Settings then Platform Feature Visibility under Preferences
  • Click on Edit button. Then enable the toggle for Enable Deployment
  • Click on Save button.
This will enable the deployment feature in the platform and allow you to create either a control plane and compute plane.
2

Choose to create a new cluster or attach an existing cluster

Go to the platform section in the left panel and click on Clusters. You can click on Create New Cluster or Attach Existing Cluster depending on your use case. Read the requirements and if everything is satisfied, click on Continue.
3

Get Domain and Certificate ARN

You need a domain and certificate ARN to point to the load balancer created in the next step. For example, if you have a domain like *.services.example.com, you will create a DNS record for it later in Step 6. We recommend using AWS Certificate Manager (ACM) to create the certificate, since it manages and renews certificates automatically. To generate a certificate ARN, follow the steps below. If you are not using AWS Certificate Manager, skip this step and continue to the next one.
  1. Navigate to AWS Certificate Manager in the AWS console.
  2. Request a public certificate.
  3. Specify your domain (e.g. *.services.example.com).
  4. Choose DNS validation (recommended).
  5. Add the CNAME records provided by ACM to your DNS provider. Follow the official AWS guide for DNS validation. For detailed steps on adding CNAME records, see the AWS documentation on DNS validation.
  6. Wait for the certificate to change to “Active” status (this may take 30 minutes or longer).
  7. Copy the certificate ARN for the next step (the format will be like arn:aws:acm:region:account:certificate/certificate-id).
4

Fill up the form to generate the OpenTofu/Terraform code

A form will be presented with the details for the new cluster to be created. Fill in with your cluster details. Click Submit when done.
The key fields to fill up here are:
  • Cluster Name - A name for your cluster.
  • Region - The region where you want to create the cluster.
  • Network Configuration - Choose between New VPC or Existing VPC depending on your use case.
  • Authentication - This is how you are authenticated to AWS on your local machine. It’s used to configure OpenTofu/Terraform to authenticate with AWS.
  • S3 Bucket for OpenTofu/Terraform State - OpenTofu/Terraform state will be stored in this bucket. It can be a preexisting bucket or a new bucket name. The new bucket will automatically be created by our script.
  • Load Balancer Configuration - This is to configure the load balancer for your cluster. You can choose between Public or Private Load Balancer, it defaults to Public. You can also add certificate ARNs and domain names for the load balancer but these are optional.
  • Platform Features - This is to decide which features like Blob Storage, Cluster Integration, Parameter Store, Docker Registry, and Secrets Manager will be enabled for your cluster. To read more on how these integrations are used in the platform, please refer to the platform features page.
Enter the domain and the certificate ARN from the previous step in the form as shown below.
5

Copy the curl command and execute it on your local machine

You will be presented with a curl command to download and execute the script. The script will take care of installing the prerequisites, downloading OpenTofu/Terraform code, and running it on your local machine to create the cluster. This will take around 40-50 minutes to complete.
6

Verify the cluster is showing as connected in the platform

Once the script is executed, the cluster will be shown as connected in the platform.
7

Create DNS Record

You can get the load balancer’s IP address by going to the platform section in the bottom left panel under the Clusters section. Under the preferred cluster, you’ll see the load balancer IP address under the Base Domain URL section.
Create a DNS record in Amazon Route 53 or your DNS provider with the following details:
8

Start deploying workloads to your cluster

FAQ

Set the tags variable in the generated OpenTofu/Terraform code to a map of your tags:
This works for both new and existing clusters — applying only adds or updates tags in place; no resources are recreated. For an existing cluster, run tofu plan (or terraform plan) first and confirm the diff is tag-only before applying.

How tagging works across all resources

Tags flow through three layers so every AWS resource — whether managed by Terraform or launched at runtime — gets consistent labels:EC2 nodes and EBS volumes: These are launched at runtime by Karpenter and the EBS CSI driver, so they can’t be tagged via Terraform state. The tags are applied through Helm values and take effect on newly launched nodes and newly provisioned PVC volumes after you apply. Existing nodes need a rolling replacement to pick up new tags.

Suppressing built-in TrueFoundry audit tags

By default, TrueFoundry modules add truefoundry-managed, truefoundry-cluster-name, and truefoundry-terraform-module tags. To suppress these without affecting your own tags or the provider default_tags, set:
This is useful if your organization enforces a strict tag allowlist.
Yes, you can use cert-manager to add TLS to the load balancer and not use AWS Certificate Manager. Follow the cert-manager getting started guide to install cert-manager and add TLS to the load balancer.
If you have your own certificate files (for example, from another certificate provider or self-signed), you can use them directly with TrueFoundry.
  1. Create a Kubernetes secret with your certificate and key, or create a self-signed certificate:
  2. Once the secret is created, head over to the cluster page and navigate to the tfy-istio-ingress add-on. Add the secret name in the tfyGateway.spec.servers[1].tls.credentialName section and ensure that tfyGateway.spec.servers[1].port.protocol is set to HTTPS. Here we are using example-com-tls as the secret name, which contains the certificate and key.
Self-signed certificates will cause browser warnings. They should only be used for testing or internal systems. To connect to services with self-signed certificates, you have to pass the CA certificate to verify the SSL certificate.
In Step 4 in the guide above, when you run the curl command, the OpenTofu/Terraform code will be downloaded to your local machine. The script will ask you before executing the OpenTofu/Terraform code at which point you can stop the execution and review the OpenTofu/Terraform code generated by the platform.
By default, Amazon VPC CNI assigns Pods an IP address from the primary subnet. If the primary subnet CIDR is too small, the CNI may not be able to acquire enough secondary IP addresses for your Pods. Custom networking solves this by using a secondary CIDR for Pod IPs. For more details, see the Amazon EKS Custom Networking documentation.Steps:
  1. Attach a secondary CIDR to the VPC (e.g. 100.64.0.0/16 per RFC 6598) and create new subnets with that CIDR in the same AZs as your primary subnets.
  2. Ensure the secondary subnets are added to the route tables of your primary subnets (this typically happens automatically).
  3. Configure the AWS VPC CNI to use the secondary subnets. If you used TrueFoundry’s OpenTofu/Terraform code, add this to the EKS module:
Otherwise, set the env variables directly:
  1. Create an ENIConfig resource for each AZ where your EKS nodes run:
  1. Restart the nodes one by one to apply the changes. Pods will be rescheduled onto nodes with secondary IP addresses.
Remove infrastructure managed by Terraform, and Kubernetes-created resources (for example load balancers and Karpenter nodes).
1

Connect to the EKS cluster

2

Delete LoadBalancer services

3

Delete Karpenter NodePools

Make sure the nodes are gone (if they are stuck, please delete them manually from the EC2 instances. This is only for the karpenter nodes):
4

From the folder with your generated OpenTofu/Terraform code

While creating the S3 bucket, terraform apply (or tofu apply) fails with an error like:
Why it happens: Your AWS Organization blocks s3:PutBucketPublicAccessBlock, but the TrueFoundry module tries to set it on each bucket — so the call is denied and the apply fails.The fix: Set the variable below to false so the module skips this step. This is safe — your account already blocks public access, so the per-bucket setting is redundant.Control plane — add truefoundry_s3_attach_public_policy = false to the tfy-control-plane module:
Compute plane — add blob_storage_attach_public_policy = false to the tfy-platform-features module:
Both variables default to true, so you must set them explicitly to false. Then re-run terraform apply (or tofu apply).
The Istio ingress service can remain in Pending state and fail to receive an external IP when the AWS account is missing the Elastic Load Balancing service-linked role and the AWS Load Balancer Controller cannot create it.
Output:
Describing the Service shows a FailedDeployModel warning:

Root Cause

The AWS account did not contain the required service-linked role: AWSServiceRoleForElasticLoadBalancing.When AWS Load Balancer Controller attempted to create a Network Load Balancer (NLB) for the Istio ingress Service, AWS tried to automatically create this service-linked role. The controller was running with an IAM role that was restricted by a permissions boundary, which prevented execution of iam:CreateServiceLinkedRole. As a result, AWS rejected the load balancer creation request and the Kubernetes Service remained in the Pending state.

Verification

Verify whether the service-linked role exists:
If the command returns NoSuchEntity, the service-linked role is missing.

Resolution

1

Create the AWS service-linked role

This is a one-time operation per AWS account. After the role exists, the AWS Load Balancer Controller does not need iam:CreateServiceLinkedRole permission to provision load balancers.
2

Confirm the Service receives an external endpoint

After the service-linked role is created, AWS Load Balancer Controller will successfully provision the NLB and the Istio ingress Service will receive an external endpoint.
The EXTERNAL-IP column should show the NLB hostname instead of <pending>.
CoreDNS handles in-cluster DNS resolution. As your cluster grows, DNS query load increases and a fixed number of CoreDNS replicas can become a bottleneck or single point of failure. EKS supports autoscaling the CoreDNS addon so the replica count scales with cluster demand.If you used TrueFoundry’s OpenTofu/Terraform code, add the cluster_addons_coredns_additional_configurations block to the EKS module:
  • enabled — turns on CoreDNS autoscaling.
  • minReplicas / maxReplicas — lower and upper bounds for the replica count. Tune these values based on your cluster size and DNS query volume.
After updating the module, run terraform apply (or tofu apply) to apply the change.
TrueFoundry provisions on-demand and spot nodes dynamically using Karpenter, configured via the karpenter-config addon. You can restrict or allow specific instance families and sizes by editing the values of the karpenter-config addon from the TrueFoundry platform (via the cluster addon configuration in the UI).Set a notAllowed list under instanceFamilies and instanceSizes to exclude specific types:
Refer to the full values.yaml for all configurable options.
In addition to Karpenter, you can add EKS managed node groups through the TrueFoundry EKS Terraform module using the additional_eks_managed_node_groups variable. For the node group to be discoverable and schedulable from the TrueFoundry platform, it must have the truefoundry.com/node-pool label — this is how TrueFoundry discovers and targets node pools.For GPU node groups, also add the nvidia.com/gpu label and a matching taint so only GPU workloads land on those nodes.
After updating the module, run terraform apply (or tofu apply) to apply the change.

Overview

This section provides ready-to-use Terraform snippets for waf.tf that remediate the following findings on an AWS WAFv2 Web ACL:Each section below is a standalone snippet — copy the block you need directly into your waf.tf. Snippets that add a rule { ... } block are meant to be inserted inside the existing aws_wafv2_web_acl.this resource, alongside the other rules; snippets that define a full resource block can be dropped anywhere in the file.
The data_protection_config block requires AWS provider >= 5.98.0. Pin this in your versions.tf:
These snippets reference 5 variables that must exist in your variables.tf: waf_app_name, waf_app_environment, waf_team, waf_it_owner_email, waf_geo_allowed_country_codes. See Required variables at the bottom of this page.

Optional trusted IP set

Defines a reusable IP set of trusted addresses. It’s only created when var.waf_ip_allowlist is non-empty, and is referenced later by the allowlist rule. Skip this snippet if you don’t need an IP allowlist.

Web ACL skeleton and default action (WAF-016)

This is the top-level aws_wafv2_web_acl resource. All rule { ... } snippets below are inserted inside this resource’s body. The default action is kept as allow — non-matching traffic passes through, and the rules that follow block bad traffic explicitly.
GIS flagged this as informational and suggested a deny-by-default (default_action { block {} }) alternative. Switching to deny-by-default requires an explicit allow rule that covers all trusted traffic first (otherwise you break the app), so it’s intentionally left as allow here. If you want to move to deny-by-default later, add a catch-all allow rule at priority 0 before flipping this.

Managed rule: Known Bad Inputs

Blocks requests matching AWS’s curated list of known malicious request patterns (e.g. exploitation of Log4j, credential stuffing patterns). Set at priority 0.

Managed rule: SQL Injection Rule Set

Applies AWS’s managed SQL injection detection ruleset across the request. Set at priority 1.

Managed rule: Amazon IP Reputation List

Blocks traffic from IP addresses that AWS’s threat intelligence has flagged as sources of botnets, scanners, or other malicious activity. Set at priority 2.

Managed rule: Common Rule Set (OWASP baseline)

Applies AWS’s Core Rule Set (broad OWASP-style protections). Two rule-level overrides are included: SizeRestrictions_BODY is set to allow (so large but legitimate request bodies aren’t blocked), and NoUserAgent_HEADER is set to count (monitor requests without a User-Agent header, rather than blocking them). Set at priority 3.

Rate limiting on /api/ traffic (Custom-Rule-85)

Remediates the Critical finding by changing the previous count {} action to block {}, and scoping the rate limit to requests whose path starts with /api/ — so UI, health check, and static asset traffic isn’t rate-limited alongside API calls. Set at priority 4.

Trusted IP allowlist rule

Allows traffic from the IP set defined earlier to bypass the rules that follow it. Only rendered when the allowlist IP set was created. Set at priority 5.
This rule sits at priority 5after the managed rule groups (priorities 0–3) and the rate-limit rule (priority 4). Allowlisted IPs are still subject to those earlier rules. If allowlisted IPs should bypass everything, move this rule to priority 0 and renumber the rest accordingly.

Managed rule: Anonymous IP List (WAF-009)

Blocks requests originating from VPNs, Tor exit nodes, proxies, and hosting providers — sources commonly used to mask the true origin of malicious traffic. Set at priority 6.
override_action is set to none {} so the rule group’s own block action applies immediately. If you’d rather observe traffic before enforcing, change this to count {} first.

Standalone SQL injection block rule (Custom-Rule-85)

A dedicated SQLi detection rule scoped to the request body, independent of the managed SQLi rule set above. Deployed in count {} (monitor-only) mode at priority 7 — check CloudWatch metrics and sampled requests for false positives before switching the action to block {}.

Standalone XSS block rule (Custom-Rule-85)

A dedicated cross-site scripting detection rule scoped to all query string arguments. Also deployed in count {} (monitor-only) mode at priority 8 — switch to block {} once validated against real traffic.

Geo-location restriction (Custom-Rule-84)

Blocks any request whose source country is not in the approved list, using a not_statement wrapped around a geo_match_statement. Configure the approved countries via var.waf_geo_allowed_country_codes. Set at priority 9.

Data protection / field redaction (WAF-021)

Substitutes the values of sensitive request headers — authorization, cookie, x-api-key, x-auth-token — before they’re written to any WAF logging destination (CloudWatch Logs, sampled requests, etc.). This block goes directly inside aws_wafv2_web_acl.this, alongside the default_action and rules.
The request body is intentionally not redacted here, to preserve forensic detail in logs. If full-body redaction is required later, add:

Logging (WAF-013, unchanged)

Creates the CloudWatch log group and logging configuration for the Web ACL. This was already compliant and is included here unchanged for completeness — the log group name must be prefixed with aws-waf-logs-, which AWS enforces.

Associating the Web ACL with an ALB

Associates the Web ACL with an Application Load Balancer. Only needed if the ALB was created outside of the AWS Load Balancer Controller — pass its ARN via var.waf_associate_alb_arn; otherwise leave that variable empty and skip this snippet.

Required variables

Add these to variables.tf if they don’t already exist (used for Custom-Rule-37 mandatory ownership/application tagging, and the geo-block rule):
  • waf_app_name
  • waf_app_environment
  • waf_team
  • waf_it_owner_email
  • waf_geo_allowed_country_codes
These are merged, along with any caller-supplied tags, into the local.waf_required_tags local referenced throughout the snippets above, and applied to every WAF resource.