Skip to content

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

Learn how to automate deployment verification in Windows Batch Scripts by checking health endpoints with HTTP status validation, retry logic, and response body checks.

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 Code Meaning
200 OK Application is healthy and ready to serve traffic
503 Service Unavailable Application is starting up or degraded
500 Internal Server Error Application is broken
No response / timeout Application 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
)

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.

Navigation