How to Force IIS 7 to Release a Locked File Without Restarting IIS


1 views

During automated build deployments, we often encounter situations where IIS 7 maintains a lock on large static files (e.g., 10MB JavaScript bundles or CSS files) even after stopping the website. This prevents our clean tasks from deleting these files, requiring a full IIS restart - which is disruptive in production environments.

The standard approach using appcmd.exe stop site doesn't always release file handles because:

  • Worker processes might still be terminating
  • HTTP.SYS kernel-mode caching may maintain references
  • Application pool recycling settings can delay shutdown

1. Force Application Pool Recycling

This is more surgical than restarting IIS:

appcmd recycle apppool /apppool.name:"YourAppPoolName"

2. Clear HTTP.SYS Cache Programmatically

Use netsh to flush the kernel cache:

netsh http flush logbuffer
netsh http delete cache

3. Scripted File Handle Release

Combine these PowerShell commands in your build script:

# Stop the site
& "$env:windir\system32\inetsrv\appcmd.exe" stop site "YourSiteName"

# Kill any lingering w3wp processes
Get-Process w3wp | Where-Object { $_.Modules.ModuleName -like "*YourSiteName*" } | Stop-Process -Force

# Wait for handles to release
Start-Sleep -Seconds 5

# Now attempt file operations
Remove-Item "C:\path\to\locked\file.js" -Force
  • Set sendCacheControlHeader="false" in web.config for static content
  • Configure application pool shutdownTimeLimit to be shorter (default is 90 seconds)
  • Consider using ARR (Application Request Routing) for better static file handling

Sysinternals Handle can identify and close specific file handles:

handle.exe -p w3wp.exe -a "lockedfile.js" -c <handle> -y

During automated build processes, we frequently encounter situations where IIS7 maintains file locks on large static content files (typically 10MB+ in size) even after stopping the associated website. The standard approach of using:

c:\\Windows\\System32\\inetsrv\\appcmd.exe stop site http://oursite.com

fails to release these locks, forcing us to restart IIS entirely - an unacceptable solution in production environments.

IIS maintains file handles for several reasons:

  • Active client downloads in progress
  • Caching mechanisms holding references
  • Background compression operations

Method 1: Using Application Pool Recycling

Instead of stopping the entire site, recycle just the application pool:

appcmd recycle apppool /apppool.name:YourAppPoolName

Method 2: Programmatic Handle Release

Create a PowerShell script to identify and close handles:

$filePath = "C:\\path\\to\\locked\\file.ext"
$handleTool = "$env:SystemRoot\\System32\\handle.exe"

# Find processes locking the file
$processes = & $handleTool -a -nobanner $filePath | 
    Select-String -Pattern "(?<Process>\w+\.exe).*pid: (?<PID>\d+)" |
    ForEach-Object { $_.Matches.Groups }

foreach ($proc in $processes) {
    Stop-Process -Id $proc.Groups["PID"].Value -Force
}

Method 3: IIS URL Rewrite Module Trick

Add a temporary rewrite rule to force release:

<rule name="ForceFileRelease" stopProcessing="true">
    <match url="lockedfile\.ext" />
    <action type="CustomResponse" statusCode="404" />
</rule>

To avoid future lock situations:

  • Implement atomic file deployment using temporary filenames
  • Set proper Cache-Control headers for static content
  • Consider using a CDN for large static files