CI/CD
Overview
Section titled “Overview”LocalStack works great in CI environments, allowing your integration tests to run incredibly fast and with no cloud costs. The setup differs slighty from local development in a few important ways:
- Use a CI Auth Token, not your personal Developer token
- Start LocalStack without a browser — Docker Compose,
docker run, or CI helpers such assetup-localstackare common;lstkalso works in CI when you setLOCALSTACK_AUTH_TOKENand use--non-interactive(or rely on automatic non-interactive detection). LocalStack Desktop remains a local-only GUI. - Runs are often isolated per job — many pipelines start from an empty LocalStack instance for reproducible tests
- Persistence in CI is supported — Cloud Pods, snapshot-based persistence with a mounted volume, or
localstack state export/localstack state importwith artifacts or cache.
Step 1 — Get a CI Auth Token
Section titled “Step 1 — Get a CI Auth Token”CI pipelines should use a dedicated CI Auth Token, not a developer token tied to a specific user.
- Go to the Auth Tokens page in the LocalStack Web Application
- Create a new CI Auth Token
- Add it as a secret in your CI provider (e.g.,
LOCALSTACK_AUTH_TOKEN)
See the Auth Token documentation for full details on token types and configuration.
Step 2 — Start LocalStack in CI
Section titled “Step 2 — Start LocalStack in CI”The recommended approach is to start LocalStack as a service container or as a step using the official GitHub Action:
name: Integration Tests
on: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Start LocalStack uses: LocalStack/setup-localstack@v0.2.2 with: image-tag: latest install-awslocal: "true" env: LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
- name: Run tests run: | # Your test commands here, e.g.: pip install awscli-local awslocal s3 mb s3://my-test-bucket pytest tests/integration/The setup-localstack action handles pulling the image, starting the container, and waiting for LocalStack to be ready.
Add LocalStack as a service in your docker-compose.yml:
services: localstack: container_name: localstack-main image: localstack/localstack-pro ports: - "127.0.0.1:4566:4566" - "127.0.0.1:4510-4559:4510-4559" - "127.0.0.1:443:443" environment: - LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:?} - DEBUG=${DEBUG:-0} volumes: - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" - "/var/run/docker.sock:/var/run/docker.sock"Start it and wait for readiness:
docker compose up -d localstack# Wait for LocalStack to be readyuntil curl -s http://localhost:4566/_localstack/health | grep -q '"running"'; do sleep 1; doneStart LocalStack directly with docker run:
docker run \ --rm -d \ --name localstack-main \ -p 127.0.0.1:4566:4566 \ -p 127.0.0.1:4510-4559:4510-4559 \ -e LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:?} \ -v /var/run/docker.sock:/var/run/docker.sock \ localstack/localstack-pro
# Wait for readinessuntil curl -s http://localhost:4566/_localstack/health | grep -q '"running"'; do sleep 1; doneAdd LocalStack as a service in your CircleCI config:
version: 2.1
jobs: integration-tests: docker: - image: cimg/python:3.12 - image: localstack/localstack-pro environment: LOCALSTACK_AUTH_TOKEN: $LOCALSTACK_AUTH_TOKEN steps: - checkout - run: name: Wait for LocalStack command: | until curl -s http://localhost:4566/_localstack/health | grep -q '"running"'; do sleep 1; done - run: name: Run tests command: pytest tests/integration/Set LOCALSTACK_AUTH_TOKEN in your CircleCI project’s environment variables.
Verify activation
Section titled “Verify activation”After LocalStack starts, confirm the license is active:
curl -s http://localhost:4566/_localstack/info | jq '.is_license_activated'# Should return: trueKey differences from local development
Section titled “Key differences from local development”| Local development | CI/CD | |
|---|---|---|
| CLI | lstk or LocalStack CLI | Docker Compose / docker run, or lstk --non-interactive |
| Auth | Browser login or stored token | LOCALSTACK_AUTH_TOKEN env var |
| Token type | Developer token | CI token |
| State | Optional persistence | Optional persistence (same mechanisms; typical pattern is a fresh instance per job) |
| Startup | Interactive TUI (default lstk) | Non-interactive (docker compose, docker run -d, lstk --non-interactive) |
Persisting state across runs
Section titled “Persisting state across runs”If you tear down the container at the end of a job, nothing is left on disk unless you save it — which is why many teams use a clean instance every time. When you do need to reuse infrastructure or data between pipeline runs or steps, CI is fully supported:
- Cloud Pods — save and load snapshots; the
setup-localstackaction can load and save pods viastate-backend: cloud-pods(see GitHub Actions — Store Localstack state). - Snapshot-based persistence — enable
PERSISTENCE=1and mount a volume so state survives container restarts on the same runner or workspace. - State export/import — run
localstack state exportandlocalstack state importand pass the file through your CI provider’s artifacts, cache, or attached storage (for example, see GitLab CI — Store Localstack state).
Choose the approach that fits your runner model: for example, multi-job GitLab pipelines often need explicit state handoff because services do not carry between jobs.
More CI integrations
Section titled “More CI integrations”LocalStack has dedicated integration guides for many CI providers:
See the full CI/CD integrations section for details.