Skip to main content

How to Verify a Deployment by Checking a Health Endpoint in Batch Script

After deploying an application, verifying that it is actually running and healthy is the final critical step before declaring success. A health endpoint is a lightweight URL (typically /health or /api/health) that returns a status code and optional diagnostics confirming the application is operational. Automating this check from a Batch Script closes the deployment loop by programmatically confirming that the new version is serving requests correctly.

In this guide, we will explore how to verify deployments by checking health endpoints from a Batch Script, including HTTP status checks, response body validation, retry logic, and automated rollback triggers.

Understanding Health Endpoints

A well-designed health endpoint returns:

Status CodeMeaning
200 OKApplication is healthy and ready to serve traffic
503 Service UnavailableApplication is starting up or degraded
500 Internal Server ErrorApplication is broken
No response / timeoutApplication is not running or not reachable

Some health endpoints return JSON with details:

{
"status": "healthy",
"version": "2.1.0",
"database": "connected",
"uptime": "00:05:32"
}

Method 1: Basic Health Check with curl

@echo off
setlocal

set "url=http://localhost:8080/health"

echo Checking health endpoint: %url%

curl -s -o nul -w "%%{http_code}" "%url%" > "%temp%\health_code.txt" 2>nul
set /p "status=" < "%temp%\health_code.txt"
del "%temp%\health_code.txt" 2>nul

if "%status%"=="200" (
echo [HEALTHY] Application is running. HTTP %status%
exit /b 0
) else if "%status%"=="000" (
echo [UNREACHABLE] Could not connect to %url%
exit /b 1
) else (
echo [UNHEALTHY] HTTP %status%
exit /b 1
)
info

curl is included in Windows 10 (build 17063+) and Windows Server 2019+. For older systems, use PowerShell's Invoke-WebRequest instead.

Method 2: Health Check with PowerShell (More Reliable)

@echo off
setlocal

set "url=http://localhost:8080/health"
set "timeout=10"

echo Checking: %url% (timeout: %timeout%s^)

powershell -NoProfile -Command ^
"try {" ^
" $r = Invoke-WebRequest -Uri '%url%' -UseBasicParsing -TimeoutSec %timeout%;" ^
" Write-Host ('[HTTP ' + $r.StatusCode + '] ' + $r.StatusDescription);" ^
" if ($r.StatusCode -eq 200) {" ^
" Write-Host '[HEALTHY] Application is running.'; exit 0" ^
" } else {" ^
" Write-Host '[WARNING] Unexpected status code.'; exit 1" ^
" }" ^
"} catch {" ^
" Write-Host ('[FAILED] ' + $_.Exception.Message); exit 1" ^
"}"

if %errorlevel%==0 (
echo Deployment verified successfully.
) else (
echo Deployment verification FAILED.
)

pause

Method 3: Health Check with Retry Logic

Applications often need a few seconds to start up after deployment. Retry the health check with delays:

@echo off
setlocal enabledelayedexpansion

set "url=http://localhost:8080/health"
set "max_retries=10"
set "retry_delay=5"
set "http_timeout=5"

echo =============================================
echo POST-DEPLOYMENT HEALTH CHECK
echo URL: %url%
echo Retries: %max_retries% (every %retry_delay%s^)
echo =============================================
echo.

set "attempt=0"

:retry
set /a attempt+=1
echo [Attempt !attempt!/%max_retries%] Checking %url%...

powershell -NoProfile -Command ^
"try {" ^
" $r = Invoke-WebRequest -Uri '%url%' -UseBasicParsing -TimeoutSec %http_timeout%;" ^
" if ($r.StatusCode -eq 200) { exit 0 } else { exit 1 }" ^
"} catch { exit 1 }"

if !errorlevel!==0 (
echo.
echo [HEALTHY] Application is up after !attempt! attempt(s^).
exit /b 0
)

if !attempt! geq %max_retries% (
echo.
echo [FAILED] Application did not become healthy after %max_retries% attempts.
exit /b 1
)

echo Not ready. Waiting %retry_delay% seconds...
timeout /t %retry_delay% /nobreak >nul
goto retry

Method 4: Full Deployment Verification Suite

Check multiple endpoints and validate response content:

@echo off
setlocal enabledelayedexpansion

set "base_url=http://localhost:8080"
set "http_timeout=5"

echo =============================================
echo DEPLOYMENT VERIFICATION SUITE
echo =============================================
echo.

set "passed=0"
set "failed=0"
set "skipped=0"

:: Check 1: Health endpoint
echo [CHECK 1] Health endpoint...
powershell -NoProfile -Command ^
"try {" ^
" $r = Invoke-WebRequest -Uri '%base_url%/health' -UseBasicParsing -TimeoutSec %http_timeout%;" ^
" if ($r.StatusCode -eq 200) { Write-Host ' PASS'; exit 0 }" ^
" else { Write-Host (' FAIL: HTTP ' + $r.StatusCode); exit 1 }" ^
"} catch { Write-Host (' FAIL: ' + $_.Exception.Message); exit 1 }"
if !errorlevel!==0 (set /a passed+=1) else (set /a failed+=1)

:: Check 2: Version endpoint (verify correct version deployed)
echo [CHECK 2] Version match...
set "expected_ver="
if exist VERSION (
set /p "expected_ver=" < VERSION
)

if not defined expected_ver (
echo SKIP (no VERSION file^)
set /a skipped+=1
) else (
set "actual_ver="
for /f "delims=" %%V in ('powershell -NoProfile -Command ^
"try { (Invoke-RestMethod -Uri '%base_url%/api/version' -TimeoutSec %http_timeout%).version }" ^
"catch { }"') do set "actual_ver=%%V"

if not defined actual_ver (
echo FAIL (version endpoint unreachable^)
set /a failed+=1
) else if "!actual_ver!"=="!expected_ver!" (
echo PASS (v!actual_ver!^)
set /a passed+=1
) else (
echo FAIL (expected: !expected_ver!, got: !actual_ver!^)
set /a failed+=1
)
)

:: Check 3: Static assets
echo [CHECK 3] Static assets accessible...
powershell -NoProfile -Command ^
"try {" ^
" $r = Invoke-WebRequest -Uri '%base_url%/' -UseBasicParsing -TimeoutSec %http_timeout%;" ^
" if ($r.StatusCode -eq 200) { Write-Host ' PASS'; exit 0 }" ^
" else { Write-Host (' FAIL: HTTP ' + $r.StatusCode); exit 1 }" ^
"} catch { Write-Host (' FAIL: ' + $_.Exception.Message); exit 1 }"
if !errorlevel!==0 (set /a passed+=1) else (set /a failed+=1)

:: Check 4: Response time
echo [CHECK 4] Response time...
powershell -NoProfile -Command ^
"$sw = [Diagnostics.Stopwatch]::StartNew();" ^
"try { Invoke-WebRequest -Uri '%base_url%/health' -UseBasicParsing -TimeoutSec 10 | Out-Null }" ^
"catch { $sw.Stop(); Write-Host (' FAIL: unreachable (' + $sw.ElapsedMilliseconds + 'ms)'); exit 1 };" ^
"$sw.Stop();" ^
"$ms = $sw.ElapsedMilliseconds;" ^
"if ($ms -lt 2000) { Write-Host (' PASS (' + $ms + 'ms)'); exit 0 }" ^
"else { Write-Host (' FAIL: slow (' + $ms + 'ms)'); exit 1 }"
if !errorlevel!==0 (set /a passed+=1) else (set /a failed+=1)

:: Summary
echo.
echo =============================================
echo RESULTS: !passed! passed, !failed! failed, !skipped! skipped
echo =============================================

if !failed! gtr 0 (
echo [WARNING] Some checks failed. Investigate before routing traffic.
exit /b 1
) else (
echo [OK] All checks passed. Deployment verified.
exit /b 0
)

Method 5: Health Check with Auto-Rollback

Automatically roll back if the health check fails after deployment:

@echo off
setlocal enabledelayedexpansion

set "url=http://localhost:8080/health"
set "app_dir=C:\WebApps\MyApp"
set "backup_dir=C:\Backups\MyApp\latest"
set "pool_name=MyApp"
set "max_retries=6"
set "retry_delay=10"
set "http_timeout=5"

echo =============================================
echo POST-DEPLOY VERIFICATION
echo =============================================
echo.

:: Wait for application startup with retries
set "attempt=0"
set "healthy=false"

:check
set /a attempt+=1
echo [!attempt!/%max_retries%] Checking %url%...

powershell -NoProfile -Command ^
"try {" ^
" $r = Invoke-WebRequest -Uri '%url%' -UseBasicParsing -TimeoutSec %http_timeout%;" ^
" if ($r.StatusCode -eq 200) { exit 0 } else { exit 1 }" ^
"} catch { exit 1 }"

if !errorlevel!==0 (
set "healthy=true"
goto result
)

if !attempt! geq %max_retries% goto result

echo Waiting %retry_delay%s...
timeout /t %retry_delay% /nobreak >nul
goto check

:result
if "!healthy!"=="true" (
echo.
echo [VERIFIED] Application is healthy after !attempt! attempt(s^).
exit /b 0
)

echo.
echo [CRITICAL] Application FAILED health checks after %max_retries% attempts.
echo.

:: Auto-rollback
if not exist "%backup_dir%\" (
echo [ERROR] No backup available for rollback at: %backup_dir%
exit /b 1
)

set "appcmd=%SystemRoot%\system32\inetsrv\appcmd.exe"

echo Initiating automatic rollback...

:: Stop app pool (if IIS is available)
if exist "!appcmd!" (
"!appcmd!" stop apppool /apppool.name:"%pool_name%" >nul 2>&1
timeout /t 3 /nobreak >nul
)

:: Restore backup
robocopy "%backup_dir%" "%app_dir%" /mir /r:2 /w:3 /np /ndl /nfl >nul
set "rb_result=!errorlevel!"

:: Restart app pool
if exist "!appcmd!" (
"!appcmd!" start apppool /apppool.name:"%pool_name%" >nul 2>&1
)

if !rb_result! leq 3 (
echo [ROLLBACK] Previous version restored.
) else (
echo [ERROR] Rollback file copy failed (exit code: !rb_result!^). Manual intervention required.
exit /b 1
)

:: Verify rollback
echo Verifying rollback...
timeout /t 5 /nobreak >nul

powershell -NoProfile -Command ^
"try {" ^
" $r = Invoke-WebRequest -Uri '%url%' -UseBasicParsing -TimeoutSec %http_timeout%;" ^
" if ($r.StatusCode -eq 200) { Write-Host '[OK] Rollback verified. Application is healthy.'; exit 0 }" ^
" else { Write-Host '[WARNING] Rollback responded but with unexpected status.'; exit 1 }" ^
"} catch { Write-Host '[WARNING] Rollback health check failed. Manual verification needed.'; exit 1 }"

exit /b 1

Common Mistakes

The Wrong Way: Checking Only the HTTP Status Code

:: INCOMPLETE - A 200 from a load balancer default page does not validate the app
curl -s -o nul -w "%%{http_code}" http://server/

Output Concern: A 200 status from the root URL might come from a load balancer default page, a static "under maintenance" page, or a previous version that was not fully replaced. Check a dedicated /health endpoint and validate that the response body contains expected content (like the correct version number).

The Wrong Way: No Retry Logic

:: WRONG - Application may still be starting
curl -s http://server/health
if %errorlevel% neq 0 echo "DEPLOYMENT FAILED!"
:: Fails immediately without giving the app time to start

Applications typically need 5–30 seconds to start after deployment. A single check immediately after deployment almost always fails. Implement retry logic with appropriate delays.

Best Practices

  1. Use a dedicated health endpoint: Do not rely on the root URL or arbitrary pages.
  2. Implement retry with backoff: Allow 30–60 seconds total for application startup.
  3. Validate response content: Check for version numbers or status fields, not just HTTP 200.
  4. Auto-rollback on failure: If health checks fail after all retries, automatically restore the backup.
  5. Check multiple aspects: Verify health, version, response time, and critical dependencies.

Conclusion

Verifying a deployment by checking health endpoints from a Batch Script completes the deployment workflow with automated confirmation that the new version is operational. By implementing retry logic for application startup time, validating response content beyond just status codes, and triggering automatic rollbacks when verification fails, teams build deployment pipelines that are self-healing and require minimal manual intervention. The health check is the final gate that determines whether a deployment is declared successful or rolled back.