TL;DR
I created shibayan/swa-deploy — a lightweight GitHub Action that only deploys to Azure Static Web Apps, without the Docker-based build overhead of the official action. It wraps the same StaticSitesClient that SWA CLI uses internally, includes automatic caching, and supports both Deployment Token and azure/login authentication.
The Problem with the Official Action
When deploying static sites (built with Astro, Vite, etc.) to Azure Static Web Apps, the standard approach is to use the official Azure/static-web-apps-deploy action that gets auto-generated when you link a GitHub repo to your SWA resource.
Unlike other Azure deployment actions (e.g., for App Service or Azure Functions), this action uses Oryx — the build engine used across Azure App Service — to build your application internally. It runs inside Docker, which means:
- The build process is a black box with hidden behaviors
- Docker image creation adds significant overhead
- Customizing the build pipeline is limited
While Oryx is a capable multi-platform build engine, the Docker-based approach makes deploys slower than they need to be, especially when you just want to push pre-built static files.
The skip_app_build Option Isn't Enough
You can set skip_app_build: true in the official action to skip the application build step. This lets you build in GitHub Actions and only deploy through the action. However, it still needs to pull/build the Docker image, so the overhead remains.
For reference, the official documentation covers this configuration:
Build configuration for Azure Static Web Apps | Microsoft Learn
Using SWA CLI: Better, But Not Ideal
To avoid the Docker overhead, I switched to using the Static Web Apps CLI for deploy-only workflows. This approach works well — it's lighter than Docker — but comes with its own trade-offs:
-
Installation time:
npm install -g @azure/static-web-apps-clitakes a while - Cache complexity: To speed things up, you need to add npm global cache steps, which adds more workflow YAML than the actual deploy
- Cognitive overhead: You end up thinking more about caching npm packages than deploying your app
The Solution: shibayan/swa-deploy
I finally decided to build a dedicated GitHub Action that does one thing well: deploy to Azure Static Web Apps.
shibayan
/
swa-deploy
A GitHub Action to deploy prebuilt frontend assets and Azure Functions APIs to Azure Static Web Apps
Deploy Azure Static Web Apps
A GitHub Action that deploys prebuilt frontend assets, Azure Functions APIs, and
staticwebapp.config.json to
Azure Static Web Apps.
It follows the same deployment model as swa deploy from the
Azure Static Web Apps CLI —
download the StaticSitesClient binary, resolve paths, and upload content using
a deployment token. When deployment-token is omitted, this action can also
resolve the token at runtime through Azure Resource Manager after azure/login
The binary is cached automatically across workflow runs.
Note
This action does not build your application. Run your build step before calling this action.
Usage
Deploy a built frontend
name: Deploy
on:
push:
branches: [master]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm ci && npm run build
- name: Deploy to Azure Static Web Apps
id: deploy…Under the hood, this action uses the same StaticSitesClient binary that SWA CLI uses. It's essentially a thin wrapper around that CLI with automatic caching built in.
Using a Deployment Token
The simplest approach — just pass your deployment token:
- name: Deploy to Azure Static Web Apps
id: deploy
uses: shibayan/swa-deploy@v1
with:
app-location: dist
deployment-token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
Using azure/login
If you prefer using azure/login with a service principal or federated credentials, pass app-name instead. The action will automatically look up the resource via the Azure Resource Manager API, retrieve the deployment token, and deploy:
- name: Azure login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy to Azure Static Web Apps
uses: shibayan/swa-deploy@v1
with:
app-location: dist
app-name: my-static-web-app
resource-group-name: my-resource-group
Note: The
resource-group-nameparameter is optional. You only need it if you have multiple SWA resources with the same name across different resource groups in your subscription — since SWA names are unique at the resource group scope, not the subscription scope.
Why Use This?
Compared to the official action and SWA CLI:
- No Docker overhead — no image build step
-
Automatic caching — the
StaticSitesClientbinary is cached automatically - Simple workflow YAML — no npm cache boilerplate
- Lightweight — does exactly one thing: deploy
I've migrated all of my SWA deployments to this action and it's been working great.
Implementation Note
The entire action was built using GitHub Copilot Chat with GPT-5.4 and Claude Opus 4.6, starting from the official template. AI-assisted development made it straightforward to implement and test.
Top comments (0)