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