Skip to content
Get Started for Free

GitHub Actions

This page contains easily customizable snippets that show you how to run the LocalStack Azure emulator in a GitHub Actions workflow.

The GitHub-hosted Ubuntu runners already provide Docker, Node.js, and the Azure CLI, so lstk is the only part that needs installing. On a self-hosted runner, add install steps for whichever of those are missing.

To run the Azure emulator, you need to add your LocalStack CI Auth Token to your repository’s secrets. lstk picks it up from the environment and passes it to the emulator.

Go to the CI Auth Token page and copy your CI Auth Token. To add the CI Auth Token to your GitHub repository, follow these steps:

  • Navigate to your repository Settings > Secrets and variables > Actions and select New repository secret.
  • Enter LOCALSTACK_AUTH_TOKEN as the name of the secret and paste your CI Auth Token as the value. Select Add secret to save your secret.

You can then install lstk and start the emulator, passing the secret to the step:

- name: Install lstk
run: npm install -g @localstack/lstk@1.2.0
- name: Start LocalStack
run: lstk start --type azure
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}

lstk start pulls the image and returns only once the emulator is ready, so no separate wait step is needed. The snippets pin lstk to the version they were tested with; bump it deliberately (see CI Best Practices). If you commit a .lstk/config.toml that sets type = "azure", you can drop --type azure. Only the steps that start the emulator need the token; the other lstk commands on this page run without it. GitHub doesn’t pass repository secrets to pull requests from forks, and gives Dependabot pull requests only Dependabot secrets, so lstk start fails on those runs with authentication required. If Dependabot opens pull requests in your repository, add LOCALSTACK_AUTH_TOKEN as a Dependabot secret as well. The complete workflow below skips pull requests from forks.

lstk setup azure prepares an Azure CLI configuration that points at the emulator, and lstk az runs your az commands against it, including Bicep deployments:

- name: Set up the Azure CLI integration
run: lstk setup azure
- name: Deploy the infrastructure
run: |
lstk az group create --name my-rg --location westeurope
lstk az deployment group create --resource-group my-rg --template-file main.bicep

If your scripts call plain az, or you deploy with Terraform, run lstk az start-interception instead. See CI Best Practices for both options.

To set emulator configuration options, pass them as LOCALSTACK_-prefixed environment variables. lstk start forwards those into the container, which strips the prefix, so LOCALSTACK_DEBUG sets the emulator’s DEBUG option. For example:

- name: Start LocalStack
run: lstk start --type azure
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
LOCALSTACK_DEBUG: "1"
LOCALSTACK_MSSQL_ACCEPT_EULA: "Y"

Settings that apply to every run belong in an [env.*] profile in .lstk/config.toml instead.

- name: Save the LocalStack logs
if: always()
run: lstk logs --verbose > localstack.log
- name: Upload the LocalStack logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: localstack-logs
path: localstack.log

if: always() makes the steps run even after a failing test, which is when the logs matter most.

The following workflow combines the snippets above. It runs as-is in any repository that has the LOCALSTACK_AUTH_TOKEN secret, and pins each action to a commit SHA:

.github/workflows/localstack.yml
name: Test against LocalStack for Azure
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
test:
# Pull requests from forks don't receive LOCALSTACK_AUTH_TOKEN, so skip them.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install lstk
run: npm install -g @localstack/lstk@1.2.0
- name: Start LocalStack
run: lstk start --type azure
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
- name: Set up the Azure CLI integration
run: lstk setup azure
- name: Deploy the infrastructure
run: |
lstk az group create --name my-rg --location westeurope
lstk az storage account create --name mystorageaccount --resource-group my-rg --location westeurope --sku Standard_LRS
- name: Run the tests
# Replace this step with your own test command.
run: lstk az storage account show --name mystorageaccount --resource-group my-rg --query provisioningState --output tsv
- name: Save the LocalStack logs
if: always()
run: lstk logs --verbose > localstack.log
- name: Upload the LocalStack logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: localstack-logs
path: localstack.log

The emulator image is published for both amd64 and arm64, and the workflow also runs unchanged on the arm64 ubuntu-24.04-arm runner. Azure SQL Database is the exception: each logical server runs Microsoft’s SQL Server image, which is published for amd64 only, so jobs that create SQL servers need an amd64 runner.

The localstack-azure-samples repository tests its samples against the Azure emulator on every pull request, with the run-samples.yml workflow. That workflow still starts the emulator with the deprecated localstack CLI, but several of its patterns are worth copying:

  • A setup job builds the test matrix dynamically, so each sample runs in its own job, and fail-fast: false keeps one failing sample from cancelling the others.
  • A sample that fails is retried once on a fresh emulator, rather than on the half-deployed state the first attempt left behind.
  • The jobs log in to Docker Hub before pulling the emulator image, to avoid 429 Too Many Requests errors when many jobs pull at the same time.
  • The jobs prune Docker before the pull, to free disk space for the emulator and the extra containers it starts.
  • Every action is pinned to a commit SHA, and permissions: {} at the workflow level grants each job only the permissions it asks for.

Running LocalStack on Windows and macOS runners

Section titled “Running LocalStack on Windows and macOS runners”

The Azure emulator runs as a Linux container. The GitHub-hosted Windows runners can’t run Linux containers, and the arm64 macOS runners, which macos-latest uses, can’t run Docker at all, because they don’t support nested virtualization. It’s currently not possible to run the emulator on either.

Was this page helpful?