Skip to content

Terraform reference stacks

deploy/terraform/ holds two independent root modules that stand up everything the control plane needs on managed services, and emit values for the Helm chart.

Stack Creates
aws/ VPC across 3 AZs, EKS with managed node groups and IRSA, RDS PostgreSQL 17 (Multi-AZ, KMS, forced TLS, PITR), S3 (versioned, SSE-KMS, lifecycle), ElastiCache for Valkey, NATS JetStream on EKS (3 replicas), KMS keys (data at rest and a secrets keyring for BYOK), IAM roles for the control plane and runner pods
gcp/ Custom VPC, regional private GKE with Workload Identity, Cloud SQL PostgreSQL 17 (regional HA, private IP, TLS), GCS, Memorystore for Valkey, NATS JetStream on GKE, Cloud KMS key ring with a secrets-keyring key, service accounts bound via Workload Identity

Both include a tainted, scale-to-zero runners node group or pool for job pods.

Terminal window
cd deploy/terraform/aws
cp terraform.tfvars.example terraform.tfvars # edit
terraform init
terraform apply -target=module.eks # cluster first: helm/kubernetes providers need it
terraform apply
$(terraform output -raw kubeconfig_command)

Install Slipway through Terraform (install_slipway = true, slipway_chart_version = "x.y.z") or with Helm using the generated values (they contain credentials; treat as a secret):

Terminal window
terraform output -raw helm_values > /tmp/slipway-values.yaml
helm install slipway oci://ghcr.io/slipway-systems/charts/slipway --version x.y.z \
-n slipway --create-namespace -f /tmp/slipway-values.yaml

Then create a runner pool in the UI and enable runners with its token (runner.enabled=true, runner.token.value=...).

Terminal window
cd deploy/terraform/gcp
cp terraform.tfvars.example terraform.tfvars # set project_id and master_authorized_networks
terraform init
terraform apply -target=google_container_cluster.slipway
terraform apply
$(terraform output -raw kubeconfig_command)

Terraform 1.9 or later; the AWS provider 6.x or the Google provider 7 or later; credentials allowed to create the resources; an ingress controller and TLS (cert-manager or a managed certificate) installed separately.

  • Defaults are production-shaped (Multi-AZ database, NAT per AZ, three nodes) and cost real money even when idle. terraform.tfvars.example has a sandbox profile.
  • deletion_protection = true guards the database and takes final snapshots. Set it to false before terraform destroy on throwaway environments.
  • Terraform state holds generated passwords. Use an encrypted remote backend.
  • Restrict the cluster endpoint (cluster_endpoint_public_access_cidrs on AWS defaults to open) or run Terraform from inside the VPC.
  • On GCP, the object store is reached over GCS’s S3-interoperability endpoint with an HMAC key, until a native GCS driver ships.
  • On GCP, Memorystore for Valkey is created without auth and TLS because the cache client does not yet support IAM auth or custom CAs. It is reachable only inside the VPC; keep the network policy restricting egress.
  • Azure and bare-metal reference stacks are planned. The Helm chart works on any Kubernetes with external services.

CI runs terraform fmt -check, init -backend=false and validate on both. Nothing in the repository applies infrastructure.