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.
Snippets
Section titled “Snippets”Start LocalStack
Section titled “Start LocalStack”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_TOKENas 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.
Deploy with the Azure CLI
Section titled “Deploy with the Azure CLI”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.bicepIf your scripts call plain az, or you deploy with Terraform, run lstk az start-interception instead.
See CI Best Practices for both options.
Configuration
Section titled “Configuration”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.
Save the LocalStack logs
Section titled “Save the LocalStack logs”- 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.logif: always() makes the steps run even after a failing test, which is when the logs matter most.
Complete workflow
Section titled “Complete workflow”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:
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.logThe 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.
Real-world example
Section titled “Real-world example”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: falsekeeps 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 Requestserrors 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.
Current limitations
Section titled “Current limitations”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.