How can I get around "file is being used by another process errors" when using custom directories with IIS in Octopus Deploy?

I have a custom install directory configured in IIS. It is common for me to see this error message:

The process cannot access the file because it is being used by another process

It doesn’t happen every time, but when it does, I have to retry which is annoying. What are some options to get around that?

First, it is important to understand why that error is happening. Since Windows Server 2003, IIS has been using Application Pools to host applications. Application pools are just like any other process in windows, it loads items up into memory, specifically .dlls. With .NET .dlls are moved in and out of memory as needed. Typically this error occurs on websites with a lot of traffic or just bad luck. I’ve seen the same site have no problems deploying and the next day at the exact same time get that error.

Please note: Some anti-viruses are known to hold onto files as well. The options below mostly apply to IIS. You will need to add exclusion rules to your files to stop the anti-virus from causing this error.

There are a few options to get around this particular issue.

Option 1 - Stop the app pool, deploy, start the app pool

Stopping the app pool will kill the process holding onto the .dlls. However, this will cause an outage to occur. If you are using a web farm, you can configure rolling deployments.

Rolling deployments allow you to do the following to each server, one at a time:

  • Remove a server from the load balancer
  • Stop the app pool
  • Do the deployment
  • Restart the app pool
  • Add the server back into the load balancer.

Option 2 - Leverage app_offline.htm

App_offline.htm is a special file for .NET. If that file appears in the root directory all traffic will immediately route to it. No other traffic will be allowed through while that file is in the root directory. Including .css and .js files. Any styling/images will have to be embedded in the app_offline.htm file.

Your process would look something like this:

  • Add app_offline.htm to root directory of the website
  • Deploy to your desired directory
  • Remove app_offline.htm

Option 3 - Use default Octopus functionality

What Octopus does out of the box is deploy to a new folder then it changes IIS to point to that new folder. This is not true for custom install directories. For example, always install MyApp to D:\Websites\MyApp. Behind the scenes the code is (essentially) doing:

  • Download and extract package to C:\Octopus\Applications[Environment Name][Package Name][Package Version Number]. For example, C:\Octopus\Applications\Test\NetCoreToDo.Web\1.0.0.12.
    In the event of multiple deployments for the same package version, it will add a _[Number] suffix to the package version number. For example, C:\Octopus\Applications\Development\OctoPetShop.Web\1.0.0.10_3.
  • Perform variable transformation
  • Update IIS to point to new directory

IIS will pick up that new directory right away and start using it. There are several upsides to doing this:

  • If a deployment fails you are still running on the previous version of the code
  • These are new .dlls in a new directory, greatly reducing the risk of the file is being used by another process error.

Now you could end up with lots of directories. But this is why retention policies were created.

Option 4 - Unique custom install directory per release

Some users have the requirement they have to deploy to the D:\Websites\Application folder. Understandable. But instead of doing that, could you install to a D:\Websites\Application[ReleaseVersion] folder? Octopus will update IIS to point the URL of the application to that new folder. It won’t have the [VersionNumber] in the URL (unless you want it).

The downside to this is Octopus doesn’t run retention policies on packages installed into custom install directories. We don’t know if it is the same directory for each deployment. We would hate to delete your website.