Skip to main content
Type: rvn-eks-cluster · Latest version: 0.9.0

Dependencies and consumers

Every dependency input can be specified manually to reference existing external infrastructure rather than a Ravion module. Set the dependency input to null and set its mapped inputs directly; see Use existing infrastructure instead of a module reference.

Readme

Production-ready Amazon EKS cluster with default managed capacity, core add-ons, and optional Fargate compute. Extend with the EKS Add-ons module (Karpenter, load balancer controller, EBS CSI, Container Insights).

Overview

Amazon EKS (Elastic Kubernetes Service) runs managed Kubernetes control planes on AWS. This module creates an EKS cluster inside your selected VPC and provisions everything a working cluster needs, in the correct order:
  1. Cluster - control plane, secrets encryption, VPC CNI, kube-proxy, Pod Identity Agent, and the AWS Load Balancer Controller IAM role; optional OIDC provider for IRSA workloads
  2. Default capacity - a managed node group for cluster components, add-ons, and workloads without stricter placement
  3. CoreDNS and Metrics Server - installed as EKS add-ons only after compute exists so their pods start healthy
  4. Optional compute - additional managed node groups and Fargate profiles
This module talks only to the AWS API, so it provisions in a single apply. It installs the Metrics Server EKS community add-on by default for CPU/memory autoscaling and kubectl top. Metrics Server is independent of the observability destinations in the separate EKS Add-ons module. Optional extensions - Karpenter autoscaling, the AWS Load Balancer Controller, the EBS CSI driver, and Container Insights - are added by that module. Terraform source: ravionhq/modules/compute/eks Metrics Server provides the Kubernetes metrics.k8s.io resource metrics API used by CPU/memory HorizontalPodAutoscalers and kubectl top. The AWS-managed community add-on is on by default and uses port 10251 for its server to avoid Fargate’s port 10250 conflict; it still needs access to kubelets on port 10250. To use an existing Metrics Server instead, set metrics_server_enabled: false under Advanced Terraform variables. You can also set metrics_server_addon_version there to pin a tested EKS add-on version; without a pin, EKS selects a compatible version when the add-on is created, not on every apply. Migration from the earlier Helm release: If a previous EKS Add-ons module installed Metrics Server through Helm, first apply that stack using a version that still manages the Helm release with metrics_server_enabled: false. Confirm the Helm release is removed, then apply this cluster version to install the EKS add-on. Do not install both managers at once; both would claim the same metrics.k8s.io API. Existing clusters without the Helm release can upgrade normally.

Use cases

Compute

Default capacity

Every cluster gets a required managed node group that provides default compute for cluster components such as CoreDNS, add-ons, and workloads without stricter placement. It defaults to 2-4 On-demand t3.medium nodes. Its settings are collapsed by default so teams using Karpenter can keep the recommended baseline without extra configuration. The minimum node count is also used as the initial size; after creation, the running node count is managed by Kubernetes autoscalers, and Terraform only enforces the min/max bounds. Each workload module can use automatic placement or target EC2 On-demand, EC2 Spot, or a dedicated Fargate profile.

Additional node groups

Add managed node groups for application workloads through the Additional node groups form. Each group independently chooses On-demand or Spot capacity, instance types, size bounds, labels, and taints. The minimum node count is also the initial node count. Add one On-demand group and one Spot group when workloads should be able to use both. Multiple instance types are especially useful for Spot because AWS can choose from more available pools.

Karpenter

This module does not create any Karpenter resources. To add Karpenter autoscaling, deploy the EKS Add-ons module referencing this cluster - it provisions everything Karpenter needs (IAM roles, Pod Identity association, instance profile, interruption queue, EventBridge rules) and installs the controller and a default NodePool.

Fargate

Fargate profiles run pods from selected namespaces on serverless compute instead of EC2 nodes. Each profile matches one or more namespace selectors, optionally narrowed by pod labels. Profiles use the cluster’s private subnets and a generated pod execution role unless you override them.

Cluster access

By default the Kubernetes API server is reachable only from inside the VPC. Enable Public endpoint access to reach it from the internet, and restrict the allowed source ranges with Public access CIDRs. A Ravion Runner security group is created automatically and allowed to reach the API endpoint on port 443. Modules that talk to the Kubernetes API (such as EKS Add-ons with Karpenter enabled) pick it up from this cluster so their pipeline runs can reach a private endpoint, so there is nothing to configure. Its ID is available as the ravion_runner_security_group_id stack output. A Ravion Runner role is also created by default: a stable IAM role registered as an EKS access entry with cluster-admin, which Ravion Runner step executions assume when they need the Kubernetes API. This keeps per-run pipeline roles out of the cluster’s access configuration — the EKS Add-ons module assumes it automatically via aws eks get-token. Its ARN is available as the ravion_runner_role_arn stack output. The trust policy admits only Ravion’s per-run pipeline runner roles in this account (role/rvn-ci/rvn-ci-*); widen it with the ravion_runner_role_trusted_principal_arns Terraform variable if other callers must assume it. EKS access entries control which IAM principals can access the cluster. The IAM principal that creates the cluster is not kept as an admin (opt back in with the bootstrap_cluster_creator_admin_permissions_enabled Terraform variable before the cluster is created; it cannot change afterwards). The plan fails if the Ravion Runner role, the creator bootstrap and the access entries would all leave the cluster without an administrator, since recovering from that needs an out-of-band eks:CreateAccessEntry call. Grant people and automation access through the Access entries form: give each entry an IAM principal ARN, then attach EKS access policies scoped to the whole cluster or to selected namespaces, and optionally map the principal to Kubernetes RBAC groups. Automatic access for Ravion deploys will be wired here in a future release.

IAM compatibility when upgrading

Add-ons use EKS Pod Identity, so the IAM OIDC provider and its certificate lookup are now off by default. Existing workloads that use IRSA need oidc_provider_creation_enabled: true under Advanced Terraform variables before upgrading to retain their provider. The cluster’s issuer URL remains available even without an IAM provider. The cluster role no longer includes AmazonEKSVPCResourceController by default. Existing Windows networking or security groups for pods need vpc_resource_controller_policy_enabled: true under Advanced Terraform variables to retain it. Ordinary Linux pod networking and multi-AZ placement do not need it. Managed node roles replace ECR ReadOnly with PullOnly, matching Karpenter nodes. This updates IAM attachments without replacing nodes. Worker-node and CNI permissions remain in place.

Networking

Zone-local routing

A multi-AZ cluster spreads pods across availability zones for resilience, but by default Kubernetes also spreads every in-cluster request across those zones, and AWS bills cross-AZ data transfer in both directions. This module keeps that traffic local by default:
  • CoreDNS is spread across zones with a topology.kubernetes.io/zone topology spread constraint, so every zone has a resolver.
  • The EKS Add-ons module patches the kube-dns Service with trafficDistribution: PreferClose, so DNS lookups go to the resolver in the caller’s zone.
  • The EKS Web Service chart sets the same trafficDistribution on each workload Service, and both the Web Service and Worker charts spread their pods across zones, so service-to-service calls stay in-zone whenever the destination has a pod there.
When a zone has no pod for the destination, traffic falls back to every zone; nothing is ever dropped. The spread is a soft preference (ScheduleAnyway), so a two-node cluster or a Spot interruption never leaves pods pending. Zone-local routing needs Kubernetes 1.31 or later; on older versions the Service field is ignored and routing behaves as before. Turn it off for the cluster by setting topology_aware_routing_enabled: false under Advanced Terraform variables; the EKS Add-ons module reads the same flag from this cluster’s outputs. Individual workloads keep the chart defaults regardless of the cluster flag.

Observability

Control plane logs (API server, audit, and authenticator by default) are shipped to CloudWatch Logs and shown on the module’s Logs tab. Adjust the log types and retention in the Observability section. Container Insights (node, pod, and container metrics plus application/data plane logs) is installed through the EKS Add-ons module; once enabled there, its data populates this module’s Metrics and Logs tabs. Control plane health metrics (API server requests and errors, pending pods, etcd database size) are published by EKS automatically at no cost and appear on the Metrics tab regardless.

Configuration

Design decisions

This module follows AWS EKS production patterns:
  • Ordered provisioning: CoreDNS is installed only after the default capacity node group exists, avoiding the add-on deadlock that occurs on clusters without compute
  • Private by default: the API endpoint is private unless public access is explicitly enabled
  • Encrypted secrets: Kubernetes secrets are envelope-encrypted with KMS by default
  • Hardened nodes: managed nodes launch with an encrypted root volume (AWS-managed key unless you supply one) and IMDSv2 with a hop limit of 1, so pods cannot read the node’s instance credentials; workloads get AWS access through Pod Identity instead
  • NetworkPolicy enforced: the VPC CNI’s network policy agent is on, so the ingress allow-list on EKS Web services actually restricts traffic
  • Least-privilege access: Ravion’s cluster-admin runner role trusts only Ravion’s per-run pipeline runner roles, and the principal that creates the cluster keeps no admin access; people are granted access through explicit access entries
  • Deletion protection on: the cluster cannot be deleted until protection is turned off, and any plan that would destroy or replace the cluster fails before anything is applied, so an upgrade can never take the cluster or its nodes away partway through
  • Node groups replaced without downtime: a change AWS can only make by recreating a node group (instance types, capacity type, AMI type, subnets) brings the new group up before the old one is drained and deleted
  • Autoscaler-friendly sizing: Terraform sets the initial node count but never fights an autoscaler over the running count
  • Pod Identity over IRSA: add-on IAM roles use EKS Pod Identity associations; creating an IAM OIDC provider for IRSA workloads is opt-in
  • Zone-local by default: CoreDNS is spread across availability zones and, together with the EKS Add-ons and workload modules, Service traffic prefers same-zone endpoints, so multi-AZ resilience does not come with cross-AZ data transfer charges on every request

Learn more

Amazon EKS Compute

Inputs reference

All inputs for rvn-eks-cluster version 0.9.0. Use the name shown for each field as the input key in module config.
$ref:rvn-aws-network
required
VPC network.
  • Immutable after creation
Set network to null. Mapped inputs for network: set these keys alongside network, not inside it, when supplying existing infrastructure instead of a module reference.
section
AWS account & region.
string
AWS account.
  • Default: << ref.input.aws_account_id >>
  • Immutable after creation
string
Region.
  • Default: << ref.input.aws_region >>
  • Immutable after creation
string
Terraform execution environment. Override the VPC, subnet, and security group for Pipeline Terraform runners. Must use the same AWS account as selected above.
  • Default: << ref.input.execution_environment_id >>
section
VPC.
string
required
VPC ID.
  • Default: <<ref.stack.output.vpc_id>>
  • Immutable after creation
string_array
required
Private subnet IDs. Subnets for the EKS control plane and default node placement. Use private subnets in at least two availability zones.
  • Default: <<ref.stack.output.private_subnet_ids>>
  • Immutable after creation
string_array
Public subnet IDs. Public subnets made available to dependent modules, such as EKS Add-ons shared load balancers. Not used by the cluster itself.
  • Default: <<ref.stack.output.public_subnet_ids>>

EKS cluster config

string
required
EKS cluster name. Name and prefix for related resources.
  • Default: <<project.given_id>>-<<environment.given_id>>
  • Immutable after creation
  • Pattern: ^[0-9A-Za-z][A-Za-z0-9-_]{0,99}$ — 1-100 letters, numbers, hyphens, and underscores. Start with a letter or number.
string
required
Kubernetes version. Kubernetes version for the EKS cluster.
  • Default: $values:first
boolean
Deletion protection. Destroy and replacement plans fail until this is turned off.
  • Default: true
boolean
Secrets encryption. Envelope-encrypt Kubernetes secrets with a KMS key. A key is created automatically unless an existing key ARN is provided below.
  • Default: true
string
Secrets KMS key ARN. Existing KMS key for Kubernetes secrets encryption. Leave blank to create a dedicated key.
  • Shown when: {"secrets_encryption_enabled":true}

Default capacity

string
Capacity type. On-demand is stable default capacity; Spot is lower cost but nodes can be interrupted.
  • Default: ON_DEMAND
  • Allowed values: ON_DEMAND (On-demand), SPOT (Spot)
string_array
required
Instance types. EC2 instance types AWS may use for this node group. Multiple types improve Spot availability.
  • Default: ["t3.medium"]
number
Minimum nodes. Minimum EC2 nodes in the default managed node group, which runs the cluster add-ons. Two keeps them highly available.
  • Default: 2
  • Min: 1
number
Maximum nodes. Maximum EC2 nodes in the default managed node group. Defaults to the minimum, a fixed group; application workloads scale on Karpenter capacity.
  • Default: 2
  • Min: 1
number
Disk size (GB). Root EBS volume size for default capacity nodes. Leave blank for the AMI default.
  • Min: 20
object_array
Taints. Keep pods without a matching toleration off this group. EKS Add-ons tolerate CriticalAddonsOnly, so that taint with No schedule reserves the group for them.
  • Default: []
object_map
Additional node groups. Add independently sized On-demand or Spot managed node groups when workloads need more or separate capacity. Create one of each to make both capacity types available.
  • Default: {}

Fargate

object_map
Fargate profiles. Run pods from selected namespaces on AWS Fargate instead of EC2 nodes.
  • Default: {}

Cluster access

boolean
Private endpoint access. Allow access to the Kubernetes API server from inside the VPC.
  • Default: true
boolean
Public endpoint access. Allow access to the Kubernetes API server from the public internet.
  • Default: false
string_array
Public access CIDRs. IPv4 CIDR blocks allowed to reach the public API server endpoint. Terraform defaults to 0.0.0.0/0.
  • Shown when: {"endpoint_public_access_enabled":true}
boolean
Network policy enforcement. Enforce Kubernetes NetworkPolicy objects through the VPC CNI. Needed for the ingress allow-list on EKS Web services; without it every NetworkPolicy in the cluster is ignored.
  • Default: true
number
Warm pod IPs per node. Spare pod IPs the VPC CNI keeps on each node. Leave blank for the CNI default, a whole spare network interface of IPs per node, which can use up a small subnet and leave new pods without an address.
  • Min: 1
number
Minimum pod IPs per node. Pod IPs the VPC CNI allocates to each node up front, so most pods start without waiting for an address. Takes effect with or without warm pod IPs.
  • Min: 0
boolean
Ravion Runner role. Create an IAM role that Ravion Runner step executions assume for Kubernetes API access, registered as an EKS access entry with cluster-admin. Used by the EKS Add-ons module to install Helm charts.
  • Default: true
object_map
Access entries. IAM principals that need cluster access. Ravion deploys use the Ravion Runner role, and the principal that creates the cluster receives no permanent access, so add an entry for the role your operators use.
  • Default: {}

Observability

string_array
Control plane log types. Control plane log types shipped to CloudWatch Logs. Remove all entries to disable control plane logging.
  • Default: ["api","audit","authenticator"]
  • Allowed values: api (API server), audit (Audit), authenticator (Authenticator), controllerManager (Controller manager), scheduler (Scheduler)
number
Log retention (days). Retention in days for the control plane CloudWatch log group.
  • Default: 365
  • Min: 1

Misc

keyvalue
Tags. A map of tags to assign to all resources. Default tags are Owner, ProjectGivenId, EnvironmentGivenId, ModuleGivenId, ModuleId

Terraform settings

string
OpenTofu version override. Override the environment’s default version for this module
string
Ravion Terraform workspace name. Override Terraform state backend workspace name. Defaults to project + environment + module given ids.
  • Immutable after creation
boolean
Include default plan policies. Turn off to plan with only the policies below instead of the default read-only policies. Requires at least one policy below.
  • Default: true
string_array
Terraform plan IAM policies. IAM managed policy ARNs for the Terraform plan runner role. Without default policies, allow s3:CreateBucket, s3:PutObject, s3:GetObject, and s3:ListBucket on the ravion-terraform-plans-<AWS account number> bucket in this account.
  • Default: []
boolean
Include default apply policies. Turn off to apply and destroy with only the policies below instead of AdministratorAccess. Requires at least one policy below.
  • Default: true
string_array
Terraform apply IAM policies. IAM managed policy ARNs for the Terraform apply runner role. Without default policies, allow s3:CreateBucket, s3:PutObject, s3:GetObject, and s3:ListBucket on the ravion-terraform-plans-<AWS account number> bucket in this account.
  • Default: []
object
Advanced Terraform variables. Optional raw Terraform variable overrides for advanced module inputs or one-off overrides. Values here override the generated variables above.
  • Default: {}