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.
Use (AWS)
Section titled “Use (AWS)”cd deploy/terraform/awscp terraform.tfvars.example terraform.tfvars # editterraform initterraform apply -target=module.eks # cluster first: helm/kubernetes providers need itterraform 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):
terraform output -raw helm_values > /tmp/slipway-values.yamlhelm install slipway oci://ghcr.io/slipway-systems/charts/slipway --version x.y.z \ -n slipway --create-namespace -f /tmp/slipway-values.yamlThen create a runner pool in the UI and enable runners with its token (runner.enabled=true, runner.token.value=...).
Use (GCP)
Section titled “Use (GCP)”cd deploy/terraform/gcpcp terraform.tfvars.example terraform.tfvars # set project_id and master_authorized_networksterraform initterraform apply -target=google_container_cluster.slipwayterraform apply$(terraform output -raw kubeconfig_command)Requirements
Section titled “Requirements”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.
Cost and safety
Section titled “Cost and safety”- Defaults are production-shaped (Multi-AZ database, NAT per AZ, three nodes) and cost real money even when idle.
terraform.tfvars.examplehas a sandbox profile. deletion_protection = trueguards the database and takes final snapshots. Set it tofalsebeforeterraform destroyon throwaway environments.- Terraform state holds generated passwords. Use an encrypted remote backend.
- Restrict the cluster endpoint (
cluster_endpoint_public_access_cidrson AWS defaults to open) or run Terraform from inside the VPC.
Known limitations
Section titled “Known limitations”- 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.