Operations 10 min read Advanced

Fixing news.hwmoon.com deployment outages with self-healing agent healthchecks

An incident review of repeated scheduled publishing and Static Web Apps deployment failures caused by self-hosted Azure Pipelines agent runtime instability, not Key Vault expiration.

Azure Pipelines Agent Runtime
Node.js 24.16.0
App Build Runtime
Node.js 22.22.3
Static Web Apps CLI
2.0.9
Healthcheck Schedule
07:50-19:50 KST, every 2 hours
Server racks and network cables in a data center
자료 이미지: Photo by Kevin Ache on Unsplash
On this page
  1. Symptoms
  2. Why It Was Not Key Vault Expiration
  3. Remediation
  4. 1. Replace runtime Key Vault task usage
  5. 2. Bypass the default checkout task
  6. 3. Remove NodeTool dependency
  7. 4. Make Static Web Apps deployment explicit
  8. Verification
  9. Scheduled Self-Healing Healthcheck
  10. Queue and Concurrency
  11. Operating Rule
  12. Remaining Risk

This incident review covers the repeated publishing and deployment failures that stopped scheduled updates on news.hwmoon.com.

At first glance, the Slack alerts looked like a Key Vault or token expiration problem. The actual logs pointed somewhere else.

The direct cause was not Key Vault expiration.
The self-hosted Azure Pipelines agent runtime was unstable.
az, npm, npx, AzureKeyVault, Checkout, and Bash task wrappers all failed with exit 139.

That distinction mattered. Extending a token lifetime would not fix a process-level segmentation fault.

Symptoms

All times below are KST.

TimePipelineRunFailing areaObservation
2026-06-14 22:00article-generation#2213AzureCLI secret loadaz crashed with exit 139
2026-06-15 08:00article-generation#2215SWA CLI deploynpx @azure/static-web-apps-cli crashed
2026-06-15 10:00article-generation#2218npm cirepeated SIGSEGV under the Node/npm runtime
validationnews-cd-static-web-app#2226Checkout taskAgent.PluginHost checkout plugin crashed
validationnews-cd-static-web-app#2229AzureKeyVault@2task Node24 wrapper crashed

The failures were not isolated to one command. The crash pattern moved across Azure CLI, npm, npx, Azure Pipelines task wrappers, and checkout.

Authentication failures usually produce 401, 403, forbidden, or secret-not-found errors. This incident produced process crashes.

Why It Was Not Key Vault Expiration

Key Vault and PAT expiration were ruled out for three reasons.

First, service principal login and Key Vault reads succeeded in runs where the local task process did not crash.

Second, the Azure DevOps PAT path worked during the incident. One generation run created and completed a pull request before the later deploy step failed.

Third, AzureKeyVault@2 itself crashed with exit 139. That points to the agent task runtime, not a secret value.

The operating conclusion was:

QuestionAnswer
Was the key expired?Not the direct cause
Should the key be set to never expire?No
What failed?self-hosted agent Node/task runtime
What should be fixed?runtime health, task avoidance, and pre-slot repair

Long-lived tokens increase security risk without addressing this failure mode.

Remediation

The fix was designed to avoid the unstable layers instead of simply retrying the same path.

1. Replace runtime Key Vault task usage

The old pipeline fetched secrets through AzureCLI@2 or AzureKeyVault@2. During this incident, those tasks were part of the failing runtime surface.

The pipeline now consumes Key Vault values through an Azure DevOps variable group:

Key Vault
  -> Azure DevOps variable group
  -> pipeline variables
  -> Bash/SWA CLI environment

The variable group is vg-hwmoon-global-insightlab-dev.

2. Bypass the default checkout task

The default checkout: self path also crashed through Agent.PluginHost. The hardened pipelines now use manual git fetch with explicit authentication and timeouts.

checkout: none
  -> git fetch with System.AccessToken
  -> PAT fallback
  -> timeout
  -> no interactive prompt

System.AccessToken is preferred. PAT is only a fallback.

3. Remove NodeTool dependency

NodeTool@0 reused a broken cache and later failed while extracting downloaded Node archives. The hardened path installs Node with Bash plus Python standard libraries.

download Node archive
  -> download SHASUMS256.txt
  -> verify sha256
  -> extract with Python tarfile
  -> prepend PATH

The app build runtime is pinned to Node.js 22.22.3. The agent task wrapper runtime is repaired to Node.js 24.16.0.

4. Make Static Web Apps deployment explicit

Static Web Apps deployment now uses the SWA CLI directly with retries and smoke checks.

npx @azure/static-web-apps-cli@2.0.9 deploy dist
  -> retry up to 3 attempts
  -> cleanup helper containers
  -> verify public routes

This makes the failure boundary clearer: npm install, static build, SWA upload, and public route validation are separate signals.

Verification

The news hardening work was merged through PR #498; the main merge commit was 5c6a694.

RunBranchResultMeaning
#2235PR branchsucceededhardened CD path validated
#2240mainsucceededproduction deployment validated
public checkhttps://news.hwmoon.com/kr/HTTP 200site route updated
archive check/kr/archive/latest article visiblecontent published

The public site showed the 2026-06-15 08:00 article after deployment.

Scheduled Self-Healing Healthcheck

A separate pipeline now repairs and warms the self-hosted agent before operating slots:

agent-runtime-healthcheck

It runs ten minutes before each two-hour publishing window.

KSTPurpose
07:50prepare for 08:00 slot
09:50prepare for 10:00 slot
11:50prepare for 12:00 slot
13:50prepare for 14:00 slot
15:50prepare for 16:00 slot
17:50prepare for 18:00 slot
19:50prepare for 20:00 slot

Azure Pipelines schedules use UTC:

schedules:
  - cron: "50 23,1,3,5,7,9,11 * * *"
    displayName: Repair and warm news deployment agent before operating slots
    branches:
      include:
        - main
    always: true

The healthcheck performs:

  • repair agent Node24 runtime to 24.16.0
  • prepare app Node runtime 22.22.3
  • verify Azure Repos authentication
  • verify npm and npx execution
  • verify SWA CLI 2.0.9
  • alert Slack if the check fails

Manual validation run #2244 succeeded.

Agent Node24 runtime is already v24.16.0.
node v22.22.3
npm 10.9.8
Repository authentication check succeeded.
SWA CLI 2.0.9
Agent runtime healthcheck completed.

Queue and Concurrency

The deployment queue was also checked. cd-console-static-web-app ran before the news deployment, so news waited in the self-hosted agent queue and then started normally.

That behavior was queue serialization, not simultaneous workspace corruption.

Still, queue delay can become publishing delay. This is why the healthcheck runs ten minutes before the scheduled publishing slot instead of at the exact same minute.

Operating Rule

For future incidents, the first decision is the failing layer.

Failure signalFirst layer to inspect
AzureKeyVault@2, AzureCLI@2, or task wrapper exit 139agent task runtime
npm ci, npx, or node SIGSEGVNode cache/runtime
401, 403, forbidden, secret not foundauthentication or Key Vault
build succeeds but deploy failsSWA token or SWA CLI
deploy succeeds but public site is stalesmoke check, cache, route, or dist path
pipeline waits in notStartedbusy/offline agent or queue contention

The key signal is exit 139. In this environment, it should be treated as a runtime crash first, not as a secret expiration.

Remaining Risk

The automation does not remove every risk.

RiskResponse
NAS host is downSlack healthcheck failure and manual NAS check
Azure DevOps outagewait/retry after service recovery
Node download failschecksum retry and next scheduled healthcheck
PAT actually expiresSystem.AccessToken first, PAT fallback failure visible
Key Vault access is removedvariable group secret download fails early

The durable fix was not an unlimited key. It was better failure classification, fewer fragile task dependencies, self-healing runtime preparation, and public route verification.

Series

Publisher Infrastructure

Part 8 of 8. This series collects related build notes so the context is easier to follow later.

  1. 1. Building domain email with Azure DNS and Zoho Mail
  2. 2. Running an Azure DevOps self-hosted agent on Synology NAS
  3. 3. Why a successful Static Web Apps pipeline still returned 404
  4. 4. Using Azure Repos as the primary source and GitHub as a backup mirror
  5. 5. Automating NAS agent operations with Slack-based AIOps
  6. 6. Preventing another news.hwmoon.com article publishing outage
  7. 7. AdSense readiness operations for news.hwmoon.com
  8. 8. Fixing news.hwmoon.com deployment outages with self-healing agent healthchecks

Related