LocalStack has become a go-to tool for teams that build on AWS but want fast, reliable, and cost-free local environments for development and testing. This guide walks you from zero to production-ready workflows with LocalStack: installing it, wiring it into your application and infrastructure code, using it in CI, and confidently promoting that code to real AWS.

Important: “Production with LocalStack” in this article means production-grade workflows (CI/CD, automated tests, infrastructure validation) that support your production AWS environment. LocalStack itself is not designed to replace AWS for serving production traffic.


Table of Contents


What is LocalStack (and What It Isn’t)?

LocalStack is a local cloud emulator that mimics many AWS services (S3, DynamoDB, SQS, Lambda, API Gateway, and more) so you can:

  • Develop and test AWS-based applications without hitting real AWS.
  • Run fast, repeatable integration and system tests in isolation.
  • Validate Infrastructure as Code templates (Terraform, CDK, CloudFormation) before deploying.

It runs entirely on your machine (or CI agents), usually via Docker.

What LocalStack is not:

  • It is not a drop-in replacement for AWS in production.
  • It does not implement every single AWS feature or edge case.
  • It should not hold any real secrets or production data.

Think of it as a very realistic simulator and test harness, not the real airplane.


Core Use Cases and Benefits

LocalStack shines in several scenarios:

  • Local development: Run your entire “cloud stack” locally, iterate quickly, avoid slow remote deploys.
  • Integration testing: Validate your application logic against emulated AWS services.
  • Infrastructure testing: Plan/apply Terraform/CDK stacks against LocalStack before deploying to AWS.
  • Onboarding: New developers can run everything with a single docker compose up.

Key benefits:

  • Near-zero cloud cost for development & testing.
  • Much faster feedback loops compared to deploying to dev AWS accounts.
  • Better reproducibility (tests do not depend on shared AWS environments).
  • Offline-friendly: work on a plane, train, or low-connectivity environment.

Prerequisites and Installation

Before you start, you should have:

  • Docker (recommended way to run LocalStack)
  • AWS CLI v2 (optional but highly recommended)
  • A recent Python (if using the CLI via pip)
  • (Optionally) Node.js, Java, or Python for your application code

Install via Docker

This is the most robust and CI-friendly option.

Pull the latest LocalStack image:

docker pull localstack/localstack

To run it quickly:

docker run --rm -it \
  -p 4566:4566 \
  -p 4510-4559:4510-4559 \
  -e SERVICES="s3,dynamodb,sqs,sns,lambda" \
  -e DEBUG=1 \
  localstack/localstack
  • 4566 is the edge port for almost all services.
  • 4510-4559 ports are used internally for some services (esp. Pro/Team).

Install via LocalStack CLI (pip)

The CLI is a lightweight wrapper that manages Docker for you.

pip install localstack

Verify:

localstack --version

Then start:

localstack start
# or for detached/background mode
localstack start -d

Note: The CLI will automatically pull and run the Docker image. You still need Docker installed.

Community vs Pro vs Team

  • Community (open source):
    • Free, GitHub-hosted.
    • Supports many core AWS services: S3, SQS, SNS, DynamoDB, Lambda (basic), API Gateway, etc.
  • Pro (paid):
    • Adds advanced services (e.g., Aurora, Kinesis, CloudWatch Logs with more fidelity, EKS, etc.).
    • Extra features like persistence, tracing, and advanced debugging.
  • Team/Enterprise:
    • Focused on large organizations: multi-user, centralized control, better auth & security, support.

For most “zero to production” workflows, Community is enough. If you need RDS, EKS, or complex data services, Pro may be worth it.


First Steps: Running LocalStack

Quick Start with the CLI

Once installed via pip:

localstack start -d     # start in background
localstack status services

You should see output listing services and their status (e.g., running).

To stop:

localstack stop

To reset all state:

localstack stop
localstack start -d
# or
localstack stop --purge   # remove volumes/containers

Using Docker Compose for Reproducible Environments

A common pattern is to define LocalStack in docker-compose.yml along with your app and dependencies.

version: "3.8"

services:
  localstack:
    image: localstack/localstack:latest
    container_name: localstack_main
    ports:
      - "4566:4566"
      - "4510-4559:4510-4559"
    environment:
      - SERVICES=s3,dynamodb,sqs,sns,lambda
      - DEBUG=1
      - LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY-} # optional for Pro
      - AWS_DEFAULT_REGION=us-east-1
    volumes:
      - "./localstack-data:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

  app:
    build: .
    environment:
      - AWS_REGION=us-east-1
      - AWS_ACCESS_KEY_ID=test
      - AWS_SECRET_ACCESS_KEY=test
      - AWS_ENDPOINT_URL=http://localstack:4566
    depends_on:
      - localstack
    ports:
      - "8080:8080"

Then:

docker compose up

Your app can reach LocalStack at http://localstack:4566 via the Docker network.


Configuring AWS CLI and SDKs to Use LocalStack

Basic AWS CLI Setup

LocalStack ignores real AWS credentials, but many tools still expect them. Use dummy values:

aws configure
# AWS Access Key ID [None]: test
# AWS Secret Access Key [None]: test
# Default region name [None]: us-east-1
# Default output format [None]: json

To target LocalStack, set the endpoint explicitly:

export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1

aws --endpoint-url=http://localhost:4566 s3 ls

To avoid repeating --endpoint-url, you can use profiles or environment variables with wrappers, but be cautious not to accidentally point production commands to LocalStack or vice versa.

Boto3 (Python) Example

import boto3
import os

LOCALSTACK_ENDPOINT = os.getenv("AWS_ENDPOINT_URL", "http://localhost:4566")
REGION = os.getenv("AWS_REGION", "us-east-1")

session = boto3.Session(
    aws_access_key_id="test",
    aws_secret_access_key="test",
    region_name=REGION,
)

s3 = session.client("s3", endpoint_url=LOCALSTACK_ENDPOINT)
ddb = session.client("dynamodb", endpoint_url=LOCALSTACK_ENDPOINT)

Tip: Always inject the endpoint through configuration (env vars, config files) rather than hard-coding.

Node.js (AWS SDK v3) Example

import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3";

const endpoint = process.env.AWS_ENDPOINT_URL || "http://localhost:4566";
const region = process.env.AWS_REGION || "us-east-1";

const s3 = new S3Client({
  region,
  endpoint,
  forcePathStyle: true, // important for LocalStack S3
  credentials: {
    accessKeyId: "test",
    secretAccessKey: "test",
  },
});

async function main() {
  const result = await s3.send(new ListBucketsCommand({}));
  console.log(result.Buckets);
}

main().catch(console.error);

Note forcePathStyle: true – LocalStack’s S3 emulation typically expects path-style buckets.


Working with Core AWS Services Locally

S3: Buckets and Objects

Create a bucket:

aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket

Upload a file:

echo "hello localstack" > hello.txt
aws --endpoint-url=http://localhost:4566 s3 cp hello.txt s3://my-bucket/hello.txt

List objects:

aws --endpoint-url=http://localhost:4566 s3 ls s3://my-bucket

Python example:

s3.put_object(Bucket="my-bucket", Key="hello.txt", Body=b"hello localstack")
resp = s3.get_object(Bucket="my-bucket", Key="hello.txt")
print(resp["Body"].read())

DynamoDB: Tables and Items

Create a table:

aws --endpoint-url=http://localhost:4566 dynamodb create-table \
  --table-name Users \
  --attribute-definitions AttributeName=UserId,AttributeType=S \
  --key-schema AttributeName=UserId,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

Put an item:

aws --endpoint-url=http://localhost:4566 dynamodb put-item \
  --table-name Users \
  --item '{"UserId":{"S":"123"}, "Name":{"S":"Alice"}}'

Scan:

aws --endpoint-url=http://localhost:4566 dynamodb scan --table-name Users

Python example:

ddb.put_item(
    TableName="Users",
    Item={"UserId": {"S": "123"}, "Name": {"S": "Alice"}},
)

resp = ddb.get_item(
    TableName="Users",
    Key={"UserId": {"S": "123"}},
)
print(resp["Item"])

SQS & SNS: Messaging Workflows

Create an SQS queue:

QUEUE_URL=$(aws --endpoint-url=http://localhost:4566 sqs create-queue \
  --queue-name my-queue \
  --query 'QueueUrl' --output text)
echo $QUEUE_URL

Send a message:

aws --endpoint-url=http://localhost:4566 sqs send-message \
  --queue-url "$QUEUE_URL" \
  --message-body "hello from localstack"

Receive messages:

aws --endpoint-url=http://localhost:4566 sqs receive-message \
  --queue-url "$QUEUE_URL"

SNS topic + subscription:

TOPIC_ARN=$(aws --endpoint-url=http://localhost:4566 sns create-topic \
  --name my-topic \
  --query 'TopicArn' --output text)

aws --endpoint-url=http://localhost:4566 sns subscribe \
  --topic-arn "$TOPIC_ARN" \
  --protocol sqs \
  --notification-endpoint "$QUEUE_URL"

aws --endpoint-url=http://localhost:4566 sns publish \
  --topic-arn "$TOPIC_ARN" \
  --message "hello via sns"

Now receive-message on the queue should show the SNS-published message.


Infrastructure as Code with LocalStack

Terraform + LocalStack

Add a Terraform provider configuration pointing to LocalStack.

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
  s3_force_path_style         = true

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

resource "aws_s3_bucket" "example" {
  bucket = "tf-localstack-bucket"
}

Then:

terraform init
terraform plan
terraform apply

You can verify:

aws --endpoint-url=http://localhost:4566 s3 ls

Tip: Maintain separate provider configurations for LocalStack vs AWS (e.g., via workspaces or separate directories) to avoid accidentally applying LocalStack configs to real AWS.

AWS CDK + LocalStack

Install the CDK and the LocalStack integration plugin.

npm install -g aws-cdk
npm install --save-dev localstack-cdk

In a simple CDK stack (TypeScript):

import * as cdk from "aws-cdk-lib";
import * as s3 from "aws-cdk-lib/aws-s3";

export class DemoStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new s3.Bucket(this, "DemoBucket", {
      bucketName: "cdk-localstack-demo-bucket",
    });
  }
}

Use localstack-cdk (or the LocalStack CLI) to deploy to LocalStack:

export LOCALSTACK_ENDPOINT=http://localhost:4566

# Option 1: Use CDK and override endpoint via env/config
cdk synth
cdk deploy \
  --profile localstack \
  --require-approval never

# Option 2 (more modern): Use localstack CLI
localstack aws cdk deploy --app "npx ts-node bin/app.ts"

The localstack aws wrapper automatically injects the LocalStack endpoint.

Serverless Framework / SAM

Serverless Framework (serverless.yml):

service: localstack-demo

provider:
  name: aws
  runtime: nodejs20.x
  stage: local
  region: us-east-1
  endpoint: http://localhost:4566
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - s3:*
          Resource: "*"

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

Deploy:

SLS_DEBUG=* serverless deploy --stage local

AWS SAM example:

sam build
sam local start-api \
  --env-vars env.json \
  --docker-network <your-network> # if you want SAM to reach LocalStack

You can also use LocalStack’s lambda emulator instead of SAM, depending on your workflow.


Designing a Local Development Workflow

Project Structure

One workable layout:

.
├── app/                   # application code
│   ├── src/
│   └── tests/
├── infra/
│   ├── terraform/         # or cdk, cloudformation, pulumi, etc.
│   └── localstack/        # local infra configs, seed scripts
├── docker-compose.yml
└── Makefile

Key principles:

  • Single command to start the entire environment (make up, docker compose up).
  • Configuration uses env vars or config files so you can easily switch between LocalStack and AWS.
  • Infra definitions for LocalStack and AWS reuse as much as possible (DRY), making divergences explicit.

Seeding and Resetting State

For reproducible tests, you often need to:

  • Create S3 buckets, DynamoDB tables, queues, etc.
  • Seed initial data (test users, sample files).
  • Reset state between test runs.

Example seed script (seed_localstack.sh):

#!/usr/bin/env bash
set -euo pipefail

ENDPOINT=${AWS_ENDPOINT_URL:-http://localhost:4566}
REGION=${AWS_REGION:-us-east-1}

export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=$REGION

# S3
aws --endpoint-url="$ENDPOINT" s3 mb s3://my-app-bucket || true

# DynamoDB
aws --endpoint-url="$ENDPOINT" dynamodb create-table \
  --table-name Users \
  --attribute-definitions AttributeName=UserId,AttributeType=S \
  --key-schema AttributeName=UserId,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST \
  >/dev/null 2>&1 || true

# SQS
aws --endpoint-url="$ENDPOINT" sqs create-queue \
  --queue-name my-app-queue \
  >/dev/null 2>&1 || true

Call this:

./seed_localstack.sh

You can run it:

  • Locally when you start the stack.
  • As part of your test setup.
  • In CI before executing the test suite.

Testing Strategy with LocalStack

Unit vs Integration vs System Tests

Use LocalStack strategically:

  • Unit tests: Don’t require LocalStack. Mock AWS SDKs, focus on your logic.
  • Integration tests: Use LocalStack to test your AWS interactions end-to-end (Lambda + SQS, S3 events, etc.).
  • System / E2E tests: Run your whole app + LocalStack + database + other services.

Rule of thumb: Only test with LocalStack what you cannot confidently test with pure mocks.

Python Example: pytest + LocalStack

Use pytest fixtures and the LocalStack CLI/Docker.

conftest.py:

import os
import subprocess
import time

import boto3
import pytest

LOCALSTACK_ENDPOINT = "http://localhost:4566"
REGION = "us-east-1"


@pytest.fixture(scope="session", autouse=True)
def localstack_env():
    # Start LocalStack via Docker compose or CLI before tests
    # Here we assume it's already running; otherwise, you can manage the lifecycle here.
    os.environ["AWS_ACCESS_KEY_ID"] = "test"
    os.environ["AWS_SECRET_ACCESS_KEY"] = "test"
    os.environ["AWS_DEFAULT_REGION"] = REGION
    os.environ["AWS_ENDPOINT_URL"] = LOCALSTACK_ENDPOINT
    yield
    # Optionally teardown/reset state


@pytest.fixture(scope="session")
def s3_client():
    return boto3.client("s3", endpoint_url=LOCALSTACK_ENDPOINT, region_name=REGION)


@pytest.fixture(scope="session", autouse=True)
def setup_localstack_resources(s3_client):
    bucket_name = "test-bucket"
    try:
        s3_client.create_bucket(Bucket=bucket_name)
    except s3_client.exceptions.BucketAlreadyOwnedByYou:
        pass
    yield

test_s3_integration.py:

def test_upload_and_read(s3_client):
    bucket = "test-bucket"
    key = "hello.txt"
    body = b"hello from pytest"

    s3_client.put_object(Bucket=bucket, Key=key, Body=body)
    resp = s3_client.get_object(Bucket=bucket, Key=key)
    assert resp["Body"].read() == body

Run:

pytest -m "integration"

You can tag tests requiring LocalStack with a marker and run them conditionally in CI.

Testcontainers + LocalStack

For JVM, Node, or Python projects, Testcontainers can manage LocalStack containers automatically in tests.

Java (JUnit 5) example:

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import software.amazon.awssdk.services.s3.S3Client;

import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3;

@Testcontainers
public class LocalstackIT {

    @Container
    private static final LocalStackContainer LOCALSTACK =
        new LocalStackContainer("localstack/localstack:latest")
            .withServices(S3);

    @Test
    void testS3() {
        S3Client s3 = S3Client.builder()
            .endpointOverride(LOCALSTACK.getEndpointOverride(S3))
            .credentialsProvider(LOCALSTACK.getDefaultCredentialsProvider())
            .region(LOCALSTACK.getRegion())
            .build();

        s3.createBucket(b -> b.bucket("test-bucket"));
        // ...
    }
}

This pattern scales well for complex integration testing in CI, as Testcontainers handles container lifecycle.


CI/CD Integration: Production-Grade Pipelines

To make LocalStack a first-class citizen in your delivery pipeline:

  1. Start LocalStack in CI (Docker service, container, or Testcontainers).
  2. Deploy or seed your infrastructure to LocalStack (Terraform/CDK/CloudFormation).
  3. Run your integration/system tests against it.
  4. Only if they pass, deploy to real AWS.

GitHub Actions Example

.github/workflows/ci.yml:

name: CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      localstack:
        image: localstack/localstack:latest
        ports:
          - 4566:4566
        env:
          SERVICES: s3,dynamodb,sqs,sns,lambda
          AWS_DEFAULT_REGION: us-east-1
        options: >-
          --health-cmd="curl -f http://localhost:4566/health || exit 1"
          --health-interval=5s
          --health-timeout=2s
          --health-retries=20

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install dependencies
        run: |
          pip install -r requirements.txt

      - name: Wait for LocalStack
        run: |
          for i in $(seq 1 30); do
            if curl -s http://localhost:4566/health | grep "\"initScripts\": \"initialized\""; then
              echo "LocalStack is ready"
              break
            fi
            echo "Waiting for LocalStack..."
            sleep 2
          done

      - name: Seed LocalStack
        env:
          AWS_ENDPOINT_URL: http://localhost:4566
        run: |
          ./infra/localstack/seed_localstack.sh

      - name: Run tests
        env:
          AWS_ENDPOINT_URL: http://localhost:4566
          AWS_ACCESS_KEY_ID: test
          AWS_SECRET_ACCESS_KEY: test
          AWS_DEFAULT_REGION: us-east-1
        run: |
          pytest -m "integration or e2e"

GitLab CI Example

.gitlab-ci.yml:

stages:
  - test

integration_tests:
  stage: test
  image: python:3.11
  services:
    - name: localstack/localstack:latest
      alias: localstack
  variables:
    AWS_ENDPOINT_URL: "http://localstack:4566"
    AWS_ACCESS_KEY_ID: "test"
    AWS_SECRET_ACCESS_KEY: "test"
    AWS_DEFAULT_REGION: "us-east-1"
  script:
    - pip install -r requirements.txt
    - ./infra/localstack/seed_localstack.sh
    - pytest -m "integration"

This pattern provides consistency between local and CI environments: same emulator, same infra scripts, same tests.


From LocalStack to Real AWS: Parity and Drift

Configuration Parity

To keep LocalStack and AWS environments aligned:

  • Use the same IaC (Terraform/CDK/CloudFormation) where possible.
  • Keep regions consistent (e.g., us-east-1 everywhere by default).
  • Parameterize environment-specific values:
    • Bucket names: my-app-${env}
    • Table names: users-${env}
    • Queue names: my-queue-${env}

Example with Terraform variables:

variable "env" {
  type    = string
  default = "local"
}

resource "aws_s3_bucket" "app" {
  bucket = "my-app-${var.env}"
}
  • For LocalStack: env = "local"
  • For dev/stage/prod AWS: env = "dev", env = "prod" etc.

Feature Gaps and Workarounds

Some AWS features are partially or not implemented in LocalStack (especially in Community edition):

  • IAM policies and complex auth flows
  • Certain managed services (Aurora, EMR, EKS, etc.)
  • Some event triggers and service integrations

Workarounds:

  • Abstract cloud interactions behind interfaces; for LocalStack, use simpler patterns that are supported.
  • Use contract tests: validate that the subset of AWS you rely on behaves the same in real AWS as it does in LocalStack.
  • For unsupported services, consider:
    • Using mocks for those parts.
    • Using a shared dev AWS account for a smaller set of tests.

Always maintain a minimal test suite against real AWS before production deploys, especially if you rely on advanced or newly released services.


Performance, Security, and Cost Considerations

Performance

  • LocalStack is fast, but heavy stacks (many services, Lambdas, large datasets) still consume CPU/memory.
  • Optimize:
    • Only enable services you need via SERVICES env var.
    • Use ephemeral stacks per test run and tear down afterwards.
    • For integration tests, prefer smaller, focused scenarios over full end-to-end flows.

Security

  • Do not store real secrets (API keys, DB passwords) in LocalStack.
  • Keep your LocalStack ports bound to localhost when running on laptops.
  • In CI, ensure LocalStack is on an isolated network and not reachable from the public internet.

Cost

  • LocalStack itself is free in Community edition.
  • Pro/Team licenses add cost but are often cheaper than large multi-account AWS dev setups.
  • Biggest savings come from:
    • Fewer dev/test AWS environments.
    • Less time and money spent on provisioning and cleaning up AWS resources for tests.

Troubleshooting and Debugging

Common issues and remedies:

  1. “Connection refused” / Cannot reach LocalStack

    • Ensure container is running: docker ps.
    • Check port mappings: 4566 should be open.
    • If using Docker Compose networks, ensure you use http://localstack:4566 inside containers, http://localhost:4566 on host.
  2. Services not available / 404 errors

    • Verify SERVICES env var includes the required service.

    • Check /health endpoint:

      curl http://localhost:4566/health
      
  3. S3 path vs virtual-host style issues

    • Use path-style configuration:
      • AWS SDKs: forcePathStyle: true (JS) / path_style = true equivalent.
      • Terraform: s3_force_path_style = true.
  4. IAM / permissions-related errors

    • LocalStack often relaxes IAM checks, but some flows still require minimal configuration.
    • For testing, start simple: allow * actions on * resources, then tighten if needed.
  5. Lambda runtime errors

    • Inspect LocalStack logs (docker logs localstack_main).
    • Ensure:
      • Handler name matches.
      • Runtime is supported.
      • Code is packaged correctly (zip or image).
    • Use DEBUG=1 for verbose logs.

When in doubt, check the LocalStack logs. They are often more informative than client error messages.


Best Practices Checklist

Use this as a quick reference when building a LocalStack-based workflow:

  • Run LocalStack via Docker (direct or via CLI) for consistency.
  • Configure AWS SDKs and CLI to use endpoint URLs from env/config, not hard-coded.
  • Keep infrastructure definitions shared between LocalStack and AWS, parameterized by environment.
  • Use LocalStack only for integration/system tests; keep pure unit tests free of it.
  • Seed and reset LocalStack state via scripts or IaC, not manual steps.
  • Wire LocalStack into CI pipelines for repeatable integration tests.
  • Maintain a small smoke-test suite against real AWS to catch emulator gaps.
  • Avoid storing real secrets in LocalStack environments.
  • Log and monitor LocalStack in CI to debug failing tests.
  • Periodically update LocalStack image and revalidate behavior to catch breaking changes early.

Conclusion

LocalStack is a powerful way to bring your AWS-based architecture close to your keyboard:

  • Developers gain fast, isolated environments without cloud friction.
  • Test suites become more robust, reproducible, and cheaper to run.
  • Infrastructure as Code pipelines become safer, as you validate changes locally before they ever touch AWS.

The key to “zero to production” success is treating LocalStack as part of your production tooling:

  • Integrate it into IaC workflows.
  • Embed it into CI/CD.
  • Use it selectively where it adds the most value (integration & system tests).

Used thoughtfully, LocalStack can dramatically improve the reliability, speed, and confidence of teams building on AWS.


Resources

Official and core documentation: