Multi-node install - Lead with the fact that the guide builds a three-node cluster, and say how to go beyond three. The callout sits inside ## Overview because the docs site discards everything above the first section heading. - Replace the OVN-only sizing prose with a table covering all three distributed layers, and move the OVN control-plane detail into the VPC networking guide, linked from here. - Add minimum and recommended hardware. - Move the object storage layout to the end as background; it was the second thing a reader hit. - Collapse the ISO conversion notes from a section into one skippable box, now that --force is unconditional in Step 4 and the formation port needs no manual intervention. - Expand verification with spx get nodes and spx top nodes, including what to look for in each. - Drop the install-node.sh shortcut (internal tooling) and the "adding a node later" section (no good story yet). Corrections found while writing - Servers 4+ were described as running no database. They run predastore and viperblock like every other node; only the OVN database cluster is capped at three. Fixed in four places. - Step 4 never needed the firewall down: spx admin init opens the formation port itself for the formation window. Only Step 3's peer-scoped OVN ports are affected. - `nft list table inet spinifex_filter | grep spinifex_peers` could never match — the peer list is an nft define, expanded at load time. Anyone following it would have seen empty output and assumed the worst. - Disambiguate "database": with predastore, NATS and OVN all in play it needed qualifying throughout. - Align the example output region with the AWS_REGION the guide exports. Cross-doc links - Rewrite relative and category-prefixed links to the canonical /docs/<slug> form. The site publishes docs flat, so ../foo/README.md and /docs/category/foo both 404. - Point the six links to unpublished docs at the source repo instead. - Fix four fragments written against GitHub's anchor algorithm.
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 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,iamandstsendpoints - Creating the EKS cluster and worker IAM roles with faithful managed-policy attachments
- Provisioning an
aws_eks_clusterand a managedaws_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_idsis 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-nodeimage;ami_typeis recorded but does not select the image. authentication_modemust be"API"(theAPI_AND_CONFIG_MAPmode 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-nodeimage available on the cluster - OpenTofu or Terraform, plus
kubectland 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 docs/terraform-workbooks/demo-app):
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 livedata "aws_eks_cluster"source, so it's only ever configured while the cluster exists. Keeping it out of the cluster module meansdestroynever tries to refresh a workload against a cluster that's already gone — the failure mode where the provider falls back tohttp://localhost:80and reportsconnection refused. Always destroyworkloads/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. KeepAWS_PROFILE=spinifexexported 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/