Introdução
Desenvolver aplicações que utilizam serviços em nuvem como AWS pode ser desafiador. Cada teste feito diretamente na cloud provider pode gerar custos e demanda tempo extra. Nesse contexto, surge o LocalStack, uma ferramenta poderosa que permite replicar localmente serviços AWS, agilizando o ciclo de desenvolvimento e reduzindo custos.
O que é o LocalStack e como funciona?
O LocalStack é uma ferramenta de código aberto que simula serviços essenciais da AWS como Lambda, S3, DynamoDB, SNS, SQS, API Gateway, entre outros. Tudo através de containers Docker. Ele emula a interface e comportamento desses serviços permitindo testes locais com alta fidelidade em relação ao ambiente real. Dessa forma, não é necessário que os desenvolvedores tenham conexão à nuvem real, proporcionando um ambiente de desenvolvimento mais rápido e seguro.
O LocalStack expõe interfaces que replicam as APIs da AWS, permitindo que ferramentas e bibliotecas que interagem com a AWS funcionem com o LocalStack com pouca ou nenhuma modificação. Por exemplo, ao usar a AWS CLI, é possível direcionar os comandos para o LocalStack especificando o endpoint local, como --endpoint-url=http://localhost:4566. Além disso, o LocalStack pode ser integrado em pipelines de CI/CD, permitindo testes automatizados de infraestrutura como código (IaC) usando ferramentas como Terraform. Essa integração facilita a validação de configurações e a detecção precoce de erros, melhorando a eficiência do ciclo de desenvolvimento.
Instalação e setup inicial
Sem muita firula já vamos para parte prática, então para começar basta seguir os passos abaixo.
Primeiramente temos que ter instalado o Docker e o AWS CLI.
O LocalStack pode ser utilizado de duas formas principais:
Instalando a ferramenta no sistema operacional
Rodando via Docker
Neste artigo vamos explorar apenas a execução via Docker Compose para melhor portabilidade. Então, no projeto onde queremos utilizar o LocalStack, vamos criar ou editar o arquivo docker-compose.yml para adicionar o serviço:
services:
localstack:
image: localstack/localstack:4
ports:
- "4566:4566"
- "4510-4559:4510-4559"
environment:
- DEBUG=${DEBUG:-1}
- PERSISTENCE=${PERSISTENCE:-1}
- LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR:-docker}
- LAMBDA_REMOVE_CONTAINERS=true
- DOCKER_HOST=unix:///var/run/docker.sock
- SERVICES=s3,lambda,rds,sts,iam,logs,cloudformation
- RDS_PORT_RANGE=5432-5433
- LAMBDA_DOCKER_NETWORK=localstack_default
- AWS_ENDPOINT_URL=http://localhost:4566
volumes:
- "./volume:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
- "./localstack-setup.sh:/etc/localstack/init/ready.d/init-aws.sh"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
Deve-se atentar a variável de ambiente “SERVICES” que contém a lista dos serviços que será utilizado. Para saber sobre os serviços veja na documentação oficial.
Após a configuração correta do docker-compose.yml, rode o serviço com docker compose up -d.
Para testar se tudo está funcionando você deve ter configurado o AWS CLI corretamente. Digite aws configure e insira uma access key e secret key qualquer, pois não será utilizada pelo local stack, e a region deixe “us-east-1” (ou outra qualquer), já o output format deixe “json”, caso prefira pode ser utilizado o recurso de profiles também. Após isso podemos executar o comando aws --endpoint-url=http://localhost:4566 s3 ls e esperar uma resposta vazia porém sem erros. Se preferir pode criar um bucket de teste e depois verificar na lista se criou corretamente: aws --endpoint-url=http://localhost:4566 s3api create-bucket --bucket test-bucket.

Exemplo prático
Para exemplificar, criaremos uma função Lambda em Go. O repositório do projeto também conterá o código da infraestrutura como código (IaC) utilizando Terraform. O projeto se trata de uma API simples em Go com um endpoint para fazer upload de arquivos txt para um bucket S3. Sempre que um arquivo for enviado para o bucket, será automaticamente disparada uma função Lambda também escrita em Go, que realizará uma ação simples, como ler e imprimir os conteúdo do arquivo no log. Dessa forma teremos o código fonte e a infraestrutura de forma real no repositório. O projeto terá a seguinte arquitetura:


Primeiro vamos criar uma api com Go, utilizando Gin. Na api teremos uma rota POST “/upload” que receberá um arquivo e o enviará para o S3. Como o foco não é a construção de uma api, manterei detalhes apenas da comunicação com a AWS. Será utilizado o SDK oficial da AWS (https://github.com/aws/aws-sdk-go), e como qualquer SDK oficial da AWS, temos a opção “Endpoint” onde podemos alterar o valor para uma url local juntamente com as opções de region, credenciais, ssl, etc. Então nossa instância do S3 terá a seguinte configuração:
s, _ := session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),
Endpoint: aws.String("http://localhost:4566"),
S3ForcePathStyle: aws.Bool(true),
})
uploader := s3.New(s)
Com isso temos o SDK apontando corretamente para o LocalStack, e é claro que essa configuração deve ser alterada para ambiente de produção ou ser controlada via variável de ambiente.
Para implantar os recursos AWS e conseguir replicar a mesma configuração em ambientes produtivos, irei utilizar Terraform. A utilização do terraform é exatamente a mesma que você teria no dia a dia, porém o provider da AWS a gente vai configurar para apontar para o LocalStack:
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
s3_use_path_style = true
skip_credentials_validation = true
skip_requesting_account_id = true
endpoints {
s3 = "http://localhost:4566"
lambda = "http://localhost:4566"
iam = "http://localhost:4566"
}
}
O projeto ficou bem simples, com apenas um arquivo (main.tf) para o terraform e os arquivos “cmd/api/main.go” e “cmd/file-processor/main.go” contendo a lógica da api e lambda respectivamente. Foi criado também um Makefile para automatizar as nossas tarefas de subir ambiente, build, etc. Então podemos rodar um “make prepare” ou apenas “make” para:
Executar o build das aplicações Go
Zipar o arquivo binário que será executado pela lambda
Iniciar o LocalStack (Docker Compose)
Provisionar a infraestrutura no LocalStack através do Terraform

Com isso já temos o projeto rodando com S3, Lambda e CloudWatch Logs completamente no LocalStack. Vamos executar um teste enviando um arquivo “teste.txt” para o endpoint da api contendo apenas um “hello world”. O código que a lambda vai executar será apenas exibir o conteúdo do arquivo nos logs. Assim como na API, o código da lambda deve adicionar a opção de endpoint apontando para o LocalStack:
func handler(ctx context.Context, s3Event events.S3Event) (string, error) {
sess, _ := session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),
Endpoint: aws.String("http://localstack:4566"),
S3ForcePathStyle: aws.Bool(true),
})
...
Uma observação é que como a lambda é executada na rede criada pelo Docker Compose, ao invés de localhost devemos colocar apenas o nome do serviço. Com isso a lambda ao ser executada já pode obter o arquivo do S3 para exibir o conteúdo. Vamos executar um curl enviando o arquivo e em seguida o comando make lambda-logs para exibir os logs da execução da lambda através do AWS CLI, mas primeiro em outro terminal devemos rodar a api com make run-api:


Perfeito! A API realizou o upload corretamente para o S3 e em seguida houve um trigger que executou a função lambda em um ambiente completamente local e muito parecido com ambiente real de produção.
Todo o código fonte deste projeto exemplo pode ser encontrado no Github: http://github.com/davidpvilaca/localstack-basics-article.
Conclusão
Utilizar o LocalStack como ambiente de desenvolvimento local para aplicações que interagem com serviços da AWS oferece inúmeras vantagens:
Eficiência: Reduz o tempo de desenvolvimento e testes, permitindo ciclos mais rápidos.
Custo: Evita custos associados ao uso de recursos reais da AWS durante o desenvolvimento.
Segurança: Permite testes em um ambiente isolado, evitando impactos em ambientes reais.
Produtividade: Facilita a automação de testes e integrações contínuas, melhorando o fluxo de trabalho.
Ao integrar o LocalStack com ferramentas como Terraform, desenvolvedores podem simular com precisão o comportamento de serviços da AWS em um ambiente local. Isso não apenas acelera o desenvolvimento, mas também proporciona uma maior confiança na qualidade e estabilidade das aplicações antes de serem implantadas em ambientes de produção.
Em resumo, o LocalStack é uma ferramenta essencial para desenvolvedores que buscam eficiência, economia e segurança no desenvolvimento de aplicações baseadas em serviços da AWS.