Files
spinifex/docs/terraform-workbooks/eks-quickstart
Josh Siviour e84753f9b9 docs: bring doc titles and descriptions into SEO length limits (#831)
* docs: bring doc titles and descriptions into SEO length limits

Rewrite the frontmatter title and description of every published doc so the
rendered <title> lands in 50-60 characters (including the site suffix) and the
meta description lands in 150-160 characters.

* docs: move SEO titles to a seoTitle field and restore display titles

The frontmatter title renders as the page H1 and the sidebar label, so tuning it
for the title tag shortened nothing and lengthened the visible navigation. Put
the search-facing string in seoTitle and give title back its original value.
2026-08-19 12:05:59 +10:00
..

title, seoTitle, description, category, tags, resources
title seoTitle description category tags resources
EKS Quickstart Terraform EKS Quickstart on Spinifex — Spinifex Docs Stand up a minimal managed Kubernetes cluster with Terraform: a VPC, IAM roles, an EKS cluster, a worker node group, an ECR repository, and a demo web app. Terraform Workbooks
terraform
eks
kubernetes
iam
vpc
workbook
title url
Terraform AWS Provider https://registry.terraform.io/providers/hashicorp/aws/latest
title url
Terraform Kubernetes Provider https://registry.terraform.io/providers/hashicorp/kubernetes/latest
title url
Spinifex Repository https://github.com/mulgadc/spinifex
title url
OpenTofu https://opentofu.org/

Terraform: EKS Quickstart

The smallest end-to-end EKS example on Spinifex: a VPC, the two IAM roles EKS needs, a one- or three-worker cluster, an ECR repository, and a Spinifex-themed demo app you can open in a browser.

Table of Contents


Overview

Provision a managed Kubernetes cluster on Spinifex with Terraform/OpenTofu — and finish with something you can actually see. As well as the cluster, this workbook creates an ECR repository, then deploys the Spinifex-themed demo app onto the cluster and publishes it on the worker's public IP. When apply finishes, open the demo_url output: the page reports the pod, node, cluster, and region that answered. Refresh it and the answering pod changes, so even a non-technical viewer can watch Kubernetes scheduling and load-balancing in real time.

Under the hood it keeps things minimal: a VPC with two public subnets, an IAM role for the control plane, an IAM role for the workers with the three managed policies AWS-managed node groups expect, a public-endpoint aws_eks_cluster, and a node group sized by node_desired_size (1 for a single-node demo, or 3 for an HA-shaped cluster). You build the demo image and push it to the ECR repository, then Terraform uses the Kubernetes provider (authenticating exactly like kubectl does, via aws eks get-token) to deploy the Deployment + NodePort Service.

What you'll learn:

  • Configuring the AWS provider to target Spinifex's eks, ec2, iam and sts endpoints
  • Creating the EKS cluster and worker IAM roles with faithful managed-policy attachments
  • Provisioning an aws_eks_cluster and a managed aws_eks_node_group
  • Pointing the Kubernetes provider at the new cluster and deploying a workload from the same apply
  • Opening one rule on the auto-managed worker security group to expose a NodePort

What gets created

Resource Name Purpose
VPC eks-quickstart-vpc Isolated network (10.30.0.0/16)
Subnets eks-quickstart-subnet-a/-b Public subnets for the cluster and worker
Internet Gateway eks-quickstart-igw Egress so the worker can pull the demo image
IAM Roles eks-quickstart-cluster-role, eks-quickstart-node-role Control-plane and worker roles
ECR Repository spinifex-demo Holds the demo image the workers pull
EKS Cluster eks-quickstart Public API endpoint, API auth mode, Kubernetes 1.32
Node Group default node_desired_size t3.medium worker(s) — 1 or 3
SG Ingress Rule eks-quickstart-demo-nodeport Opens the demo NodePort on the auto-managed worker SG
K8s Deployment spinifex-demo The themed demo image from ECR, 2 replicas
K8s Service spinifex-demo NodePort publishing the demo on the worker

Spinifex specifics

  • The cluster's security groups are auto-managed — vpc_config.security_group_ids is ignored. To reach a NodePort, this workbook looks the worker SG up by its deterministic name (eks-cluster-<name>-nodegroup-sg) and adds a single ingress rule.
  • The worker AMI is always Spinifex's eks-node image; ami_type is recorded but does not select the image.
  • authentication_mode must be "API" (the API_AND_CONFIG_MAP mode is rejected).
  • The workers need outbound internet (here via the IGW) to pull the demo container image.

Prerequisites:

  • Spinifex installed and running (see Installing Spinifex)
  • The Spinifex eks-node image available on the cluster
  • OpenTofu or Terraform, plus kubectl and the AWS CLI
  • Docker, to build and push the demo image to ECR

Instructions

Step 1. Get the Template

git clone --depth 1 --filter=blob:none --sparse https://github.com/mulgadc/spinifex.git spinifex-tf
cd spinifex-tf
git sparse-checkout set docs/terraform-workbooks
cd docs/terraform-workbooks/eks-quickstart

Or create a main.tf file and paste the full configuration below.

Step 2. Deploy

The cluster and the demo app are two root modules with separate state. Apply the cluster first:

export AWS_PROFILE=spinifex
tofu init
tofu apply

This creates the cluster (which bootstraps a control-plane VM and brings up k3s — a few minutes in CREATING), launches the worker(s), creates the spinifex-demo ECR repository, and opens the NodePort.

Set the worker count with node_desired_size (1 for a single node, 3 for an HA-shaped cluster):

tofu apply -var node_desired_size=3

Once the cluster is ACTIVE, build and push the demo image to the ECR repository this created (full commands in ../demo-app/README.md):

cd ../demo-app
REGISTRY=$(cd ../eks-quickstart && tofu output -raw ecr_repository_url)
REGISTRY_HOST=${REGISTRY%%/*}
aws ecr get-login-password | docker login --username AWS --password-stdin "$REGISTRY_HOST"
docker build -t "${REGISTRY}:latest" .
docker push "${REGISTRY}:latest"
cd ../eks-quickstart

Then deploy the demo app from the nested workloads/ module — it defaults demo_image to the parent's ECR repository at :latest:

cd workloads
tofu init
tofu apply

Why two modules? The Kubernetes provider in workloads/ reads the cluster endpoint from a live data "aws_eks_cluster" source, so it's only ever configured while the cluster exists. Keeping it out of the cluster module means destroy never tries to refresh a workload against a cluster that's already gone — the failure mode where the provider falls back to http://localhost:80 and reports connection refused. Always destroy workloads/ before the cluster.

Same profile for the Kubernetes provider. The Kubernetes provider authenticates by shelling out to aws eks get-token, which has to reach the Spinifex STS endpoint. Keep AWS_PROFILE=spinifex exported for both applies.

Step 3. Open the Demo

Run from the cluster module directory (cd .. if you're still in workloads/):

tofu output demo_url

Open that URL in a browser. You'll see the Spinifex-themed page reporting the pod, node, cluster, and region that handled the request. Refresh a few times — with two replicas, the pod name alternates, demonstrating that the Service is load-balancing across the cluster.

Step 4. Inspect with kubectl (optional)

aws eks update-kubeconfig --name eks-quickstart --region ap-southeast-2
kubectl get nodes
kubectl get pods -o wide

kubectl get pods -o wide shows the demo pods and which node each landed on.

Cleanup

Destroy in reverse — the demo app first (while the cluster is still up), then the cluster:

cd workloads
tofu destroy
cd ..
tofu destroy

Troubleshooting

Demo URL Doesn't Load

The page is served from a NodePort on the worker's public IP. Work through the chain:

# Is the worker running with a public IP?
aws ec2 describe-instances --filters "Name=tag:spinifex:eks-cluster,Values=eks-quickstart" \
  --query 'Reservations[].Instances[].[InstanceId,State.Name,PublicIpAddress]' --output text

# Did the demo pods roll out?
kubectl get pods -o wide

# Is the NodePort rule on the worker SG?
aws ec2 describe-security-groups --filters "Name=group-name,Values=eks-cluster-eks-quickstart-nodegroup-sg" \
  --query 'SecurityGroups[0].IpPermissions'

If the pods are Pending, the worker may not be Ready yet — give it a moment. If they're ImagePullBackOff, the worker can't pull from ECR: confirm you built and pushed the image (tofu output ecr_repository_url), that the IGW route and the worker's public IP are in place, and that the node role carries AmazonEC2ContainerRegistryReadOnly.

kubernetes provider: connection refused / Unauthorized

The provider runs aws eks get-token against the Spinifex STS endpoint. Confirm the AWS CLI is still pointed at Spinifex and the cluster is ACTIVE:

aws sts get-caller-identity
aws eks describe-cluster --name eks-quickstart --query 'cluster.status'

If the cluster was still CREATING when the provider first tried to connect, just re-run tofu apply.

Cluster Stuck in CREATING

Control-plane bootstrap takes a few minutes. Confirm the underlying VM came up:

aws eks describe-cluster --name eks-quickstart --query 'cluster.status'
aws ec2 describe-instances --profile spinifex

Provider Connection Refused

sudo systemctl status spinifex.target
curl -k https://localhost:9999/