Speeding Up Azure Static Web Apps Builds With Caching
Improving Azure Static Web Apps' default GitHub Action for Node.js projects.
Background
Static Web Apps is Azure’s service for hosting a static site with automatic TLS certificates, a global CDN, preview environments, and (optionally) serverless functions.
When you push a code change to your site’s source repository, a workflow builds and deploys the new version to Azure.
To accomplish this, when you link your repository to your Static Web App in the Azure portal, the wizard commits a GitHub Actions workflow
to your project.
The GitHub Actions Workflow
At the time of writing, the default Static Web Apps deployment workflow looks like this:
build_and_deploy_job identifies your project type, runs its build command, and uploads the output to Azure.
close_pull_request_job deletes preview environments when you close a pull request.
The build and deploy step of this file works perfectly in most cases, but it is quite slow.
It uses an open-source, first-party tool called Oryx to
generate a build script specific to your project structure.
Splitting The Install, Build, and Deploy Steps
Oryx combines all three phases of the workflow into one action: installing packages, building the application, and uploading it to Azure.
It supports many different build systems and frameworks—currently, it supports .NET, Go, Java, Node.js, PHP, Python, and Ruby.
With so many supported platforms, implementing caching for each one is likely out of scope for the project.
When researching possible solutions for Node.js package caching, I found these two GitHub issues:
Looking back at the workflow file that Azure provides, the GitHub Action that they use is Azure/static-web-apps-deploy@v1.
That repository led me to the workflow configuration documentation.
Through these docs, I found out that you could skip building the app and only use the action to deploy it. If we handle building the app ourselves, we can take full control over caching and speed up our builds.
Here’s What I Did
I added skip_app_build: true to the inputs (the with: block) of the Azure/static-web-apps-deploy step.
I configured two steps to run before the Static Web App deploy step:
These steps replicate what Oryx would have done if we didn’t set skip_app_build to true.
I set up Next.js caching using GitHub’s first-party cache action and NPM package caching using the option in the setup-node action:
Make sure to place these steps before the install and build steps!
Final Product
In the end, my workflow file looked like this (modified lines highlighted):
The project I wrote this workflow for is built with Next.js. This solution should work for any Node.js project with an NPM build script;
however, you may need to adjust the APP_LOCATION variable to fit your framework.
For me, the variable’s value didn’t matter because Oryx automatically deploys Next.js apps from the .next or .next/standalone directories.
Next Steps
⚠️ Before following my instructions in this section, please make sure the repository is not deprecated! This is (hopefully) a temporary solution.
The Azure Static Web Apps deploy action runs in a Docker container, so every time it’s invoked, it pulls a Docker image (mcr.microsoft.com/appsvc/staticappsclient) to create a container.
In September 2022, according to a GitHub issue comment I found,
this image was 2.41GB. Now, it’s 1.12GB, but there is still much room for improvement.
The commenter shared their own drop-in replacement for Azure’s GitHub Action and Docker image that only weighs 291MB (about a quarter of the official image’s current size). I have not used this in my own projects, but this could shave some time off your builds and therefore reduce GitHub Actions billed minutes—and if you use your own GitHub Actions runners, it would greatly reduce your bandwidth costs.
To use it, replace the line that reads:
with this:
AJ’s action is a fork of the official one that points to their slimmer image instead of Microsoft’s.
Further Reading
I found a similar implementation in this extremely helpful article by John Reilly, which also includes workflow steps for running Playwright tests against the deployed SWA staging environment URLs.