Skip to Content

Install onto EKS

Backed by RDS Postgres 16 (external or provisioned) + bundled Redis, fronted by an ALB. Two postures via deploy_cluster, mirroring GKE.

Reuse path (deploy_cluster = false, default) — must already exist

  • The cluster itself, reachable with eks:DescribeCluster and mapped to Kubernetes RBAC (an EKS access entry or aws-auth ConfigMap).
  • The AWS Load Balancer Controller, already installed — without it, apply succeeds but no load balancer is ever created.
  • The EBS CSI driver, enabled as an EKS addon — unlike GKE, EKS does not enable this by default; without it the bundled Redis PVC never binds.
  • A managed RDS Postgres 16 instance, unless create_rds = true (provisioning path only).

Provisioning path (deploy_cluster = true) sets up automatically

  • Networking — dedicated VPC, public+private subnets per AZ, one NAT gateway (cost tradeoff over per-AZ HA), subnets tagged for ALB controller auto-discovery.
  • The EKS cluster — private-only by default (cluster_endpoint_public_access = false), matching GKE’s posture.
  • A bastion (default on) — no public IP, no open inbound ports, reached only via aws ssm start-session. Terraform/kubectl/helm run from the bastion once the endpoint is private-only.
  • Two node groupsspaces (default m5.xlarge) and control_plane (default m5.2xlarge — not smaller, same sizing lesson as GKE), with the same taint_control_plane_pool toggle shape.
  • OIDC provider + IRSA for the EBS CSI addon and a Helm-installed AWS Load Balancer Controller.
  • create_rds = true (opt-in) — a small db.t4g.micro instance, single-AZ.

Registry — direct pull by default; Harbor as an opt-in fallback

Pulls straight from registry.marshal.codes. There is no ECR pull-through cache option — confirmed live that CreatePullThroughCacheRule only accepts a hardcoded enum of known upstreams, unlike GCP Artifact Registry’s REMOTE_REPOSITORY mode, which accepts any Docker v2 host. See Registry options for the full picture.

Harbor cache (create_harbor_cache) — opt-in fallback, what it sets up

Use only when direct-pull’s shared rate limit becomes a real problem — not a default-on replacement.

  • Harbor deployed in-cluster (Helm), pinned to the control-plane node group; no vulnerability scanner; blob storage on S3 via IRSA.

  • A harbor_registry + harbor_project (public/anonymous-read, safe since only reachable inside the private VPC) target registry.marshal.codes via Harbor’s generic docker-registry adapter.

  • Nodes reach it via a fixed ClusterIP (not a hostname — node-level containerd can’t resolve *.svc.cluster.local); Harbor serves plain HTTP internally since a raw IP has no valid cert hostname to match.

  • Configuring the proxy needs one manual step — the Harbor Terraform provider can only reach the ClusterIP from an actual node, not the bastion, so background a port-forward first:

    kubectl port-forward -n harbor-cache svc/harbor 8080:80 & terraform apply

Quickstart

Configure terraform.tfvars

cp ../examples/aws-eks.tfvars.example terraform.tfvars $EDITOR terraform.tfvars

Plan and apply

terraform init terraform plan # review terraform apply

Verify

terraform output next_steps

Optional storage buckets

create_audit_persistence_bucket = true # compliance export create_spaces_persistence_bucket = true # session pause/resume snapshots

Not yet built: reusing an existing VPC (create_network = false) — only create_network = true is exercised so far.

Day-2

Bump chart_version and terraform apply. Roll back with helm rollback marshal -n marshal.