Files
Ben Duncan adb3d9876d fix: read a tagged bucket's tags through S3 instead of S3 Control (#1109)
The AWS provider reads a bucket's tags with `ListTagsForResource` on S3 Control when it knows the account ID. S3 Control puts the account in the hostname — `https://{account}.{endpoint}` — so against an IP-addressed endpoint the request fails a DNS lookup before a packet leaves, and no endpoint configuration can help. A tagged bucket therefore fails its first read after being created.

`skip_requesting_account_id` alone does not fix it: `aws-sdk-go-base` takes the account ID from the STS `GetCallerIdentity` credentials check before it consults that flag, so `skip_credentials_validation` is needed alongside it. With no account ID the provider falls back to `GetBucketTagging`, which predastore serves.

`s3-webapp` is the only workbook that creates a bucket, and it uses no `aws_caller_identity` and references no other security group, so a blank account ID costs it nothing.

The bucket also gets `force_destroy`, since the webapp uploads into it and a destroy of a used bucket fails `BucketNotEmpty` without it.

The e2e harness now asserts the bucket carries the tags Terraform set, which is what catches this regressing, and no longer aborts the remaining workbooks on a failure — `s3-webapp` failing says nothing about whether RDS works, and four nights of `rds-quickstart` coverage were lost that way. `STOP_ON_FAIL=1` restores the early abort.

Every workbook's provider versions are pinned, so a run reproduces rather than picking up whatever the registry published that morning.
2026-09-26 16:11:00 -05:00
..

title, seoTitle, description, category, tags, resources
title seoTitle description category tags resources
Nginx Web Server (Load Balanced) Load-Balanced Nginx Behind an AWS ALB — Spinifex Docs Deploy a VPC with two private EC2 instances running Nginx behind an internet-facing Application Load Balancer on Spinifex, using Terraform or OpenTofu. Terraform Workbooks
terraform
nginx
ec2
elbv2
alb
vpc
workbook
title url
Terraform AWS Provider https://registry.terraform.io/providers/hashicorp/aws/latest
title url
Spinifex Repository https://github.com/mulgadc/spinifex
title url
OpenTofu https://opentofu.org/

Terraform: Nginx Web Servers with ALB

Deploys two Nginx web server instances behind an Application Load Balancer on Spinifex.

Table of Contents


Overview

Deploy two Nginx web servers behind an internet-facing Application Load Balancer on Spinifex using Terraform/OpenTofu. This workbook provisions a VPC with public and private subnets, an internet gateway and NAT Gateway, route tables, security group, SSH key pair, an application load balancer (ALB) and two EC2 instances with cloud-init user-data that installs and starts Nginx. Only the ALB is reachable from outside the VPC — the Nginx instances live in the private subnets and reach the internet only for cloud-init bootstrapping via the NAT Gateway.

Nginx + ALB VPC — IGW, two public subnets with ALB ENIs and NAT GW, two private subnets hosting nginx

What you'll learn:

  • Configuring the AWS Terraform provider to target Spinifex
  • Creating a VPC with public + private subnets, an IGW and a NAT Gateway
  • Provisioning a multi-AZ internet-facing ALB fronting private workers
  • Provisioning an EC2 instance with cloud-init user-data
  • Generating SSH key pairs with the TLS provider

What gets created

Resource Name Purpose
VPC nginx-alb-vpc Isolated network (10.20.0.0/16)
Public Subnets nginx-alb-public-a, nginx-alb-public-b Two AZs hosting the ALB and NAT Gateway
Private Subnets nginx-alb-private-a, nginx-alb-private-b Two AZs hosting the Nginx workers
Internet Gateway nginx-alb-igw Routes internet traffic for the public subnets
Elastic IP nginx-alb-nat-eip Public address for the NAT Gateway
NAT Gateway nginx-alb-nat Outbound internet for the private subnets (cloud-init apt bootstrap)
Security Group nginx-alb-sg Allows SSH (22) and HTTP (80) inbound
EC2 Instances nginx-alb-1, nginx-alb-2 Ubuntu 26.04 with Nginx via cloud-init (private subnets)
ALB nginx-alb Internet-facing Application Load Balancer on port 80
Target Group nginx-alb-tg HTTP health-checked group for both instances
Listener HTTP :80 Forwards traffic to the target group

Prerequisites:

Instructions

Step 1. Get the Template

Clone the Terraform examples from the Spinifex repository:

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
cd docs/terraform/nginx-alb

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

Step 2. Install Debian AMI

Install the Debian 13 AMI which is used in the example to host the nginx webservers as an EC2 instance.

spx admin images import --name debian-13-x86_64

Note: The load balancer itself runs as a direct-boot QEMU microvm using the kernel + initramfs bundled with the Spinifex distribution (/usr/share/spinifex/microvm/), so no separate LB AMI import is required.

Step 3. Deploy

export AWS_PROFILE=spinifex
tofu init

Step 4. Specify instance and apply

Next, depending on your architecture and CPU/memory requirements you must specify an instance type to launch.

Either specify an instance type directly (e.g Intel)

# AMD instance
export TF_VAR_instance_type="t3a.small"

# Or, Intel
export TF_VAR_instance_type="t3.small"

Or alternatively, using the AWS CLI tool query your instance for available types (e.g Intel, AMD, ARM) that support 2 vCPUs and 1 GB RAM.

export TF_VAR_instance_type=$(aws ec2 describe-instance-types \
  --query "sort_by(InstanceTypes[?VCpuInfo.DefaultVCpus==\`2\` && MemoryInfo.SizeInMiB>=\`1024\`], &MemoryInfo.SizeInMiB)[0].InstanceType" \
  --output text)

Next, apply and launch the template:

tofu apply

Step 5. Verify

Note: EC2 instances can take 30+ seconds to boot after apply, and the NAT Gateway must be available before cloud-init on the workers can reach the apt repository. If the ALB returns 5xx or HTTP is unreachable, wait and retry — the target group health checks need a moment to mark both instances healthy once Nginx has installed.

The ALB is internet-facing, but the DNS name Spinifex returns (*.elb.spinifex.local) will not resolve from your host. Fetch the ALB's public IP with the AWS CLI:

ALB_IP=$(aws elbv2 describe-load-balancers --names nginx-alb \
  --query 'LoadBalancers[0].AvailabilityZones[].LoadBalancerAddresses[].IpAddress' \
  --output text)

Then hit the ALB — successive requests should alternate between Server 1 and Server 2:

curl http://$ALB_IP
curl http://$ALB_IP

Open http://$ALB_IP in your browser and refresh to see the page alternate content served from each instance.

The Nginx instances themselves only have private IPs (see the instance_1_private_ip / instance_2_private_ip outputs) and are only reachable from inside the VPC — go through the ALB.

Check target health via AWS CLI:

TG_ARN=$(aws elbv2 describe-target-groups \
  --query 'TargetGroups[0].TargetGroupArn' \
  --output text)

aws elbv2 describe-target-health --target-group-arn $TG_ARN

Cleanup

tofu destroy

Troubleshooting

AMI Not Found

Ensure you have imported an Ubuntu 26.04 image. Check available AMIs:

aws ec2 describe-images --owners 000000000000 --profile spinifex

If missing import:

spx admin images import --name ubuntu-26.04-x86_64

Provider Connection Refused

Verify Spinifex services are running:

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

ALB Returns 5xx / Targets Unhealthy

Give the instances a moment to finish cloud-init (Nginx has to install before it can answer health checks). Check target health:

TG_ARN=$(aws elbv2 describe-target-groups --names nginx-alb-tg \
  --query 'TargetGroups[0].TargetGroupArn' --output text)
aws elbv2 describe-target-health --target-group-arn "$TG_ARN"

If targets stay unhealthy, verify the instances are running:

aws ec2 describe-instances --profile spinifex

If cloud-init on the workers never finished, confirm the NAT Gateway is available (the private subnets rely on it for outbound apt access):

aws ec2 describe-nat-gateways --query 'NatGateways[].[NatGatewayId,State]'

Nginx Not Responding

The Nginx instances have no public IP, so you can't SSH in directly from your host. If you need to inspect cloud-init logs, launch a small jump host in the same VPC or run commands via the Spinifex console, then:

ssh -i nginx-alb-demo.pem ubuntu@<instance_private_ip>
sudo journalctl -u cloud-init --no-pager
sudo systemctl status nginx