C#, .Net and Azure

Azure App Service: Run from package

2020/03/14

App Services (for windows) has supported deployment mode run from package (previously named run from zip) for a while and I have been using it for all my deployments.

It has quickly become my favorite way of deploying web apps for many reasons:

It deploys everything as a single zip file that is then directly mounted as a virtual directory via a custom driver. This has a bunch of advantages compared to the other supported deployment methods (zip deploy, MS Deploy, FTP, ..).

Note: Do not confuse ZipDeploy with Run from package. While both upload a zip file ZipDeploy will extract the files and place them in the wwwroot directory. Meanwhile Run from package will store the zip file and then mount it directly as a virtual directory.

With ZipDeploy you may still have lingering files from previous deployment as by default it doesn't remove other files whereas with Run from package only the files from the current zip will be available.

Uploading a single (compressed) zip is obviously faster than uploading many tiny files. With Run from package the startup time is also improved (especially when many small files need to be loaded from disk) as all files streamed from the same zip (a continuous blob on disk which reducing disk seek and cache misses).

There are two basic modes for run from package deployment type:

The app service can either load the zip from any publicly available url (e.g. Azure Blob Storage with SAS token) or the zip can be uploaded directly to the web app itself.

Option 1: WEBSITE_RUN_FROM_PACKAGE=<url>

The first option means you must upload the file somewhere (blob storage, S3, ..) and it is then downloaded on every restart of the application (e.g. on every IIS recycle, during up/downscale operations, ..). Since the web app may restart at any time you must allow access to the url for as long as you wish the app to be able to run. The app service caches the package only during runtime (on restart/upscale the new app service instance must download the file from the url again).

In most cases this means you need a long-lived SAS token (unless you deploy regularly in which case you may risk using a shorter duration).

I am not a fan of this mode since it requires the url to be publicly available (without authentication) for the app service to download the zip (at best you can keep the url with SAS token hidden and lock it down with IP restrictions but IPs are shared across many app services).

Additionally it has impact on the startup performance of the web app since on every restart the zip file must first be downloaded from the url - the bigger the zip, the longer the startup time (with self contained .Net Core Deployments 100MB+ packages are not rare).

Option 2: WEBSITE_RUN_FROM_PACKAGE=1

That is why the second option exists and is the one that should always be used (in my opinion): The zip file will be uploaded directly to the web app during deployment. It is stored in a separate folder (D:\home\data\SitePackages) where it is cached making it available on every restart/upscale (last 5 zips are kept on the app service and the rest are cleaned up automatically for you).

Before you set the environment variable WEBSITE_RUN_FROM_PACKAGE to 1 you should upload the zip file and must also create a file named packagename.txt in the SitePackages folder. This file must contain exactly the name of the zip to run from (no whitespaces, no newlines). Once the environment variable is set/the app service is restarted your old wwwroot directory (and its content) will disappear and instead the content of the zip will be visible thanks to the driver mounting the zip file as a virtual directory.

Note: The original files in wwwroot are not actually deleted. To get access to them again just remove the environment variable and restart the app service.

Updating is now as simple as uploading a new zip file and changing the name inside the packagename.txt file (this will trigger an instant swap of the zips and the new app will startup).

The easiest way to use Run from package deployment is to use an automated pipeline like Github actions (uses Run from package=1 by default) or Azure DevOps (default depends on the target).

Note: When using ARM templates to deploy the app service/function app make sure to also include the WEBSITE_RUN_FROM_PACKAGE variable in it or to completely disable variable configuration in the ARM template. If you have it configured and do not include the variable then it is removed on deployment and will make your app stop (as it will restart and fallback to loading the original wwwroot directory).

Overall run from package has a lot of benefits compared to regular deployments:

Disadvantages:

I actually consider the readonly part to be a benefit as well: In a corporate environment it prevents developers from making "quick changes" in production (that are then promptly forgotten about) and instead forces them to go through the regular deployment process.

If you often find yourself making minor changes in an app service because "it's easier than running through the release process" then perhaps your release process needs to be streamlined.

I can have a new version live 5 minutes after pushing a change as all my opensource code is using github actions (I could also easily get it under 2 minutes by using a custom build server/caching and skipping the ARM deployment).

Finally read more about the feature in the old official announcement and the documentation.

tagged as .Net Core, Azure and App Service