Why you need LocalStack
Learning Terraform on real AWS means:
- Registering an AWS account and attaching a credit card.
- The constant worry of "I forgot to kill that EC2 instance, now I owe 30 bucks."
- Real credentials that can leak through git.
- Slow operations: spinning up an RDS instance takes 15 minutes.
LocalStack solves this. It runs a Docker container that pretends to be AWS. It has the same API, the same HTTP endpoints, the same JSON responses. Terraform sees no difference: to it, this is AWS.
What Terraform on LocalStack can do (community edition):
- S3: buckets, objects, lifecycle, versioning.
- EC2: instances, VPC, subnets, security groups.
- IAM: roles, policies, users.
- Lambda: functions, layers, alias.
- SQS, DynamoDB, SNS, CloudWatch Logs, STS: basic functionality.
What it cannot do (community): EKS, ECS Fargate, Cognito, KMS (partial), RDS (only partial). The full list is on the LocalStack site.
How Terraform connects to it
The idea is simple. By default the AWS provider talks to the public
AWS endpoints, addresses like https://s3.us-east-1.amazonaws.com. We retrain
it to talk to the LocalStack address. Inside the docker network of our sandbox
that is http://localstack:4566 (the DNS name of the neighboring container).
You do this through the endpoints block in the provider:
provider "aws" {region = "us-east-1"
access_key = "test"
secret_key = "test"
# Without these flags the provider will go check creds against real AWS and fail.
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
# S3 in LocalStack works only with path-style URLs.
s3_use_path_style = true
endpoints {s3 = "http://localstack:4566"
ec2 = "http://localstack:4566"
iam = "http://localstack:4566"
lambda = "http://localstack:4566"
sqs = "http://localstack:4566"
dynamodb = "http://localstack:4566"
sts = "http://localstack:4566"
cloudwatch = "http://localstack:4566"
logs = "http://localstack:4566"
}
}
In a sandbox session of the course this block already sits ready to use at
/opt/skeletons/aws-localstack-provider.tf and is copied automatically
into the lesson directory. All you have to write is the resources. You do not
touch the configuration.
If you run LocalStack locally outside the course, the container name may
be different. In that case change the address to the one the container is reachable
on from the outside, for example http://localhost:4566 with a plain
docker run -p 4566:4566 localstack/localstack:3.x.
A look at the flags
-
access_key = "test",secret_key = "test". LocalStack accepts any credentials. The literals"test"are a convention from the LocalStack documentation. -
skip_credentials_validation = true: the AWS provider usually calls STS to validate credentials. In LocalStack STS works, but poorly, so it is better to turn it off. -
skip_metadata_api_check = true: the provider tries to read EC2 instance metadata (in case we are running inside EC2). On a local machine this is pointless and it slows down the start. -
skip_requesting_account_id = true: without it the provider calls STS for the account ID; in LocalStack that is just an extra request. -
s3_use_path_style = true. S3 supports two URL formats:- subdomain-style:
https://my-bucket.s3.amazonaws.com/key - path-style:
https://s3.amazonaws.com/my-bucket/key
LocalStack handles only path-style, so this flag is mandatory.
- subdomain-style:
The template we use in the course
In this course the sandbox container automatically runs:
cp /opt/skeletons/aws-localstack-provider.tf provider.tf
The skeleton file already contains the whole block you need. All you have to write is the resources:
# provider.tf is already here, do not touch it
# main.tf is what you write
resource "aws_s3_bucket" "hello" {bucket = "my-first-bucket"
tags = {Owner = "student"
}
}
Checking that LocalStack works
The healthcheck returns the status of every service:
curl http://localstack:4566/_localstack/health
# {"services": {"s3": "available", "ec2": "running", ...}}If you see available or running, the service is ready. If you see disabled or an error, something is wrong with the container.
Gotchas
-
State from LocalStack will not move over to real AWS. If you practiced locally and then switched to real AWS, you cannot just copy
terraform.tfstateacross. It is a snapshot of "pseudo-AWS"; those resources do not exist in real AWS. -
Not every resource is one-to-one. Sometimes an attribute that real AWS returns comes back from LocalStack in a different format. The
terraform_state_resourcetests can pass on LocalStack but fail on real AWS. Keep that in mind when you move over. -
Persistence is off by default. If you restart the LocalStack Docker container, all your buckets are gone. That is handy for learning (you always get a clean environment), but it surprises you if you do not know about it. You can keep the data with
PERSISTENCE=1in the LocalStack env. -
endpointsis a hack. In a real project there should be noendpointsin the HCL. If you see them in someone else's code, it is either a test environment or the smell of a production problem. -
Community vs Pro. LocalStack Pro is paid and emulates more services (EKS, IAM Identity Center, KMS). The course uses only community, the free one.