# Test Terraform Locally with LocalStack: S3, SQS and DynamoDB Without an AWS Account

> Run LocalStack in Docker, point the AWS CLI and the Terraform AWS provider to it and create S3, SQS and DynamoDB resources locally for free, fast tests.

- Source: https://www.itwonderlab.com/localstack-terraform-aws-local/
- Published: 2026-08-20
- Updated: 2026-08-20
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Why LocalStack

Testing infrastructure code against a real AWS account costs money and time, and a mistake can have consequences. [LocalStack](https://www.itwonderlab.com/localstack/) runs a local emulation of many [AWS](https://www.itwonderlab.com/glossary/aws-services/) services in one [Docker](https://www.itwonderlab.com/docker/) container. You can run `terraform apply` against it in seconds, in a laptop or in CI, and throw everything away.

> [!NOTE]
> LocalStack is an emulator. Services such as S3, SQS, SNS and DynamoDB behave very closely to AWS, but check IAM and unusual features in a real account before production.

## Start LocalStack

```bash
docker run -d --name localstack -p 127.0.0.1:4566:4566 localstack/localstack
curl -s http://localhost:4566/_localstack/health | jq
```

The health endpoint lists the services. Everything is available at `http://localhost:4566` (the edge endpoint). See [jq](https://www.itwonderlab.com/jq-cheat-sheet-aws-cli-kubectl/) for the filter.

With Docker Compose:

```yaml title="compose.yaml"
services:
  localstack:
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"
    volumes:
      - localstack-data:/var/lib/localstack
volumes:
  localstack-data:
```

## Use the AWS CLI

Any credentials work. Set dummy values and the endpoint:

```bash
export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_DEFAULT_REGION=us-east-1
aws --endpoint-url=http://localhost:4566 s3 mb s3://demo-bucket
aws --endpoint-url=http://localhost:4566 s3 ls
aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name jobs
```

The `awslocal` wrapper (`pip install awscli-local`) adds the endpoint for you.

## Point Terraform to LocalStack

```hcl title="main.tf"
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region                      = "us-east-1"
  access_key                  = "test"
  secret_key                  = "test"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true
  s3_use_path_style           = true

  endpoints {
    s3       = "http://localhost:4566"
    sqs      = "http://localhost:4566"
    dynamodb = "http://localhost:4566"
  }
}

resource "aws_s3_bucket" "assets" {
  bucket = "demo-assets"
}

resource "aws_sqs_queue" "jobs" {
  name = "jobs"
}

resource "aws_dynamodb_table" "orders" {
  name         = "orders"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "id"

  attribute {
    name = "id"
    type = "S"
  }
}
```

```bash
terraform init
terraform apply -auto-approve
aws --endpoint-url=http://localhost:4566 dynamodb list-tables
```

`s3_use_path_style` is needed because the emulator has no wildcard DNS for `bucket.localhost`. Add one `endpoints` line for each service you use. The same configuration works with [OpenTofu](https://www.itwonderlab.com/opentofu/).

## One configuration for local and real AWS

Use a variable so the endpoints appear only in tests:

```hcl
variable "use_localstack" {
  type    = bool
  default = false
}

provider "aws" {
  region                      = "us-east-1"
  access_key                  = var.use_localstack ? "test" : null
  secret_key                  = var.use_localstack ? "test" : null
  skip_credentials_validation = var.use_localstack
  skip_requesting_account_id  = var.use_localstack
  s3_use_path_style           = var.use_localstack

  dynamic "endpoints" {
    for_each = var.use_localstack ? [1] : []
    content {
      s3       = "http://localhost:4566"
      sqs      = "http://localhost:4566"
      dynamodb = "http://localhost:4566"
    }
  }
}
```

Run `terraform apply -var use_localstack=true` for local tests. See [dynamic blocks](https://www.itwonderlab.com/terraform-dynamic-blocks/).

## Test with `terraform test`

The native test framework ([Terraform testing](https://www.itwonderlab.com/terraform-testing-opentofu-test/)) can run against LocalStack in CI: start the container, run `terraform test`, stop the container. Pair it with the scanners in [Terraform security scanning](https://www.itwonderlab.com/terraform-security-scanning-tflint-checkov-trivy/).

## Reset and clean up

```bash
terraform destroy -auto-approve
docker rm -f localstack
```

Removing the container discards all the emulated data unless you mounted a volume for persistence.

## Limits

- The free edition covers the common services; some advanced services need a paid plan.
- IAM policies are not enforced by default.
- Always run a final test in a sandbox AWS account.
