How to Recycle an Application Pool in IIS from Batch Script
Recycling an application pool is one of the most important IIS maintenance operations. It gracefully shuts down the current worker process (w3wp.exe) and spins up a fresh one, clearing accumulated memory, releasing stale file handles, and resetting application state. Unlike stopping and starting a pool (which creates a brief outage), recycling uses an overlapping strategy where the new worker process starts before the old one finishes, enabling near-zero-downtime restarts.
In this guide, we will explore how to recycle application pools from a Batch Script using appcmd.exe, including targeted recycling, scheduled recycling, and monitoring techniques.
Understanding Recycling vs. Stop/Start
| Operation | Downtime | What Happens |
|---|---|---|
| Recycle | Near zero | IIS starts a new worker process, then gracefully shuts down the old one. Requests are seamlessly handed over. |
| Stop + Start | Several seconds | The pool stops completely (all requests fail), then restarts. Clients receive errors during the gap. |
| iisreset | 3-10+ seconds | The entire IIS server stops and restarts, affecting all websites. |
Recycling is almost always the preferred option for production environments.
Method 1: Simple Application Pool Recycle
@echo off
setlocal
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "pool_name=DefaultAppPool"
:: Verify Admin
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Administrator privileges required.
pause
exit /b 1
)
echo Recycling application pool "%pool_name%"...
"%appcmd%" recycle apppool /apppool.name:"%pool_name%"
if %errorlevel% equ 0 (
echo [SUCCESS] "%pool_name%" has been recycled.
) else (
echo [ERROR] Failed to recycle. Pool may be stopped or does not exist.
)
pause
endlocal
What Happens During a Recycle
- IIS creates a new worker process (
w3wp.exe) for the pool. - New incoming requests are routed to the new process.
- The old worker process finishes handling its remaining in-flight requests.
- Once the old process is idle (or a shutdown timeout is reached), it terminates.
- The pool is now running on a fresh process with clean memory.
Method 2: Recycling All Application Pools
For server-wide maintenance, you may want to recycle every pool on the machine.
@echo off
setlocal enabledelayedexpansion
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
net session >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)
echo Recycling ALL application pools...
echo.
set "count=0"
set "skipped=0"
for /f "tokens=*" %%P in ('"%appcmd%" list apppool /text:name') do (
echo Recycling: %%P
"%appcmd%" recycle apppool /apppool.name:"%%P" >nul 2>&1
if !errorlevel! equ 0 (
echo [OK]
set /a "count+=1"
) else (
echo [SKIP] Pool may be stopped.
set /a "skipped+=1"
)
)
echo.
echo [DONE] Recycled !count! pool(s), skipped !skipped!.
pause
endlocal
A pool that is in the Stopped state cannot be recycled. The script above gracefully skips stopped pools rather than failing.
Method 3: Post-Deployment Recycle
The most common use case for recycling is after deploying new application code. Recycling forces the .NET CLR to reload updated DLLs from disk.
@echo off
setlocal
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "pool_name=ProductionApiPool"
set "deploy_source=\\BuildServer\Releases\API\Latest\*"
set "deploy_target=C:\inetpub\sites\api"
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)
echo =============================================
echo ZERO-DOWNTIME DEPLOYMENT
echo =============================================
echo.
:: Step 1: Copy new files (the old worker still serves requests)
echo [1/2] Deploying new files...
xcopy "%deploy_source%" "%deploy_target%\" /s /e /y /q >nul
if %errorlevel% neq 0 (
echo [ERROR] File copy failed. Aborting deployment.
pause
exit /b 1
)
echo Files copied.
:: Step 2: Recycle the pool (seamless transition to new code)
echo [2/2] Recycling pool "%pool_name%"...
"%appcmd%" recycle apppool /apppool.name:"%pool_name%"
if %errorlevel% equ 0 (
echo.
echo [SUCCESS] Deployment complete. New code is live.
) else (
echo.
echo [ERROR] Recycle failed. Manual intervention may be needed.
)
pause
endlocal
Why Recycling Works for Deployments
When you copy new .dll files into the application directory, the currently running worker process still has the old DLLs loaded in memory. The new files are sitting on disk but unused. When the pool recycles, the new worker process loads the fresh DLLs from disk, effectively activating the new code. This is why recycling after deployment is essential.
Method 4: Recycling with Verification
A robust script should verify the recycle was successful by checking that the Process ID (PID) of the worker changed.
@echo off
setlocal enabledelayedexpansion
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "pool_name=DefaultAppPool"
net session >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)
:: Get the current worker process PID before recycle
set "old_pid="
for /f "tokens=*" %%P in ('"%appcmd%" list wp /apppool.name:"%pool_name%" /text:wp.name 2^>nul') do (
set "old_pid=%%P"
)
if not defined old_pid (
echo [INFO] No active worker process. Pool may be idle or stopped.
echo Recycling anyway...
"%appcmd%" recycle apppool /apppool.name:"%pool_name%"
pause
exit /b 0
)
echo Current Worker PID: !old_pid!
echo Recycling...
"%appcmd%" recycle apppool /apppool.name:"%pool_name%"
if !errorlevel! neq 0 (
echo [ERROR] Recycle command failed.
pause
exit /b 1
)
:: Wait for the new worker to spin up
echo Waiting for new worker process...
set /a "attempts=0"
set "max_attempts=10"
:pid_check
timeout /t 2 /nobreak >nul
set /a "attempts+=1"
set "new_pid="
for /f "tokens=*" %%P in ('"%appcmd%" list wp /apppool.name:"%pool_name%" /text:wp.name 2^>nul') do (
set "new_pid=%%P"
)
if defined new_pid (
if not "!new_pid!" == "!old_pid!" (
echo [SUCCESS] Worker recycled. Old PID: !old_pid! -^> New PID: !new_pid!
pause
exit /b 0
)
)
if !attempts! lss !max_attempts! goto pid_check
if defined new_pid (
echo [WARNING] PID unchanged after !max_attempts! checks. Recycle may still be in progress.
) else (
echo [WARNING] No new worker process detected. Pool may be idle (no requests yet^).
)
pause
endlocal
Configuring Automatic Recycling Rules
Instead of manually recycling pools, you can configure automatic recycling schedules via appcmd.
Time-Based Recycling
@echo off
setlocal
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "pool_name=ProductionPool"
:: Recycle every 6 hours (360 minutes)
"%appcmd%" set apppool /apppool.name:"%pool_name%" /recycling.periodicRestart.time:"06:00:00"
if %errorlevel% equ 0 (
echo [OK] Pool will auto-recycle every 6 hours.
) else (
echo [ERROR] Failed to set recycling interval.
)
pause
endlocal
Schedule-Based Recycling (Specific Times)
@echo off
setlocal
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "pool_name=ProductionPool"
:: Disable interval-based recycling (set to 0) so only scheduled times apply
"%appcmd%" set apppool /apppool.name:"%pool_name%" /recycling.periodicRestart.time:"00:00:00"
:: Add scheduled recycles at 2 AM and 2 PM
"%appcmd%" set apppool /apppool.name:"%pool_name%" /+recycling.periodicRestart.schedule.[value='02:00:00']
"%appcmd%" set apppool /apppool.name:"%pool_name%" /+recycling.periodicRestart.schedule.[value='14:00:00']
if %errorlevel% equ 0 (
echo [OK] Pool will auto-recycle at 2:00 AM and 2:00 PM daily.
) else (
echo [ERROR] Failed to set recycling schedule.
)
pause
endlocal
Memory-Based Recycling
@echo off
setlocal
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "pool_name=LeakyAppPool"
:: Recycle when private memory exceeds 1 GB (value in KB)
"%appcmd%" set apppool /apppool.name:"%pool_name%" /recycling.periodicRestart.privateMemory:1048576
if %errorlevel% equ 0 (
echo [OK] Pool will auto-recycle when memory exceeds 1 GB.
) else (
echo [ERROR] Failed to set memory-based recycling.
)
pause
endlocal
Common Mistakes
The Wrong Way: Using IISRESET Instead of Recycling
:: WRONG - Restarts the ENTIRE IIS server
iisreset /restart
Output Concern:
iisreset stops and restarts all IIS services, affecting every website and pool on the server. If you only need to refresh one application, recycling its specific pool is the correct, surgical approach.
The Wrong Way: Recycling a Stopped Pool
:: WRONG - A stopped pool has no worker to recycle
"%appcmd%" recycle apppool /apppool.name:"StoppedPool"
You cannot recycle a pool that is not running. The command will return an error. Start the pool first with appcmd start apppool, then recycle if needed.
Best Practices
- Recycle instead of stop/start: Recycling provides near-zero-downtime transitions, while stop/start creates a gap.
- Recycle after code deployments: New DLLs are only loaded when the worker process restarts. Recycling ensures the new code is active.
- Set memory-based auto-recycling: For applications known to have memory leaks, configure
privateMemorylimits to recycle before the server runs out of RAM. - Verify with PID checks: After recycling, confirm the worker process PID changed to ensure the recycle actually completed.
- Schedule during low traffic: If using scheduled recycling, target early morning hours when request volume is lowest to minimize the impact of the brief transition period.
Conclusion
Recycling application pools from a Batch Script is the preferred maintenance operation for production IIS servers. The appcmd recycle apppool command initiates a graceful worker process replacement that keeps the site responsive during the transition. For automated environments, configuring time-based, schedule-based, or memory-based recycling rules ensures pools stay healthy without manual intervention. Combined with post-deployment verification and PID monitoring, recycling scripts form the backbone of professional IIS lifecycle management.