Skip to content

Deployment Lock Overlay

When a Coolify deployment is running for a user-facing Logrite service, every logged-in user of app.logrite.ai sees a non-dismissable full-screen overlay that blocks all interaction and shows live progress. The overlay auto-closes 1 minute after the deploy completes (to let traffic settle), then the app unlocks.

This replaces the earlier read-only /deployments dashboard (reverted because it polled Coolify continuously even when nothing was happening).

The lock triggers only for these services:

  • frontend-main
  • backend-main
  • analyzer-main, analyzer-dev
  • ai-service

Infrastructure and pipeline deploys (OpenSearch, OpenSearch Dashboards, Data Prepper, Docs, Webhook) do NOT lock the UI. The gated-set is defined at the top of Logrite-UI/src/components/DeploymentLock/useDeploymentLock.js.

Coolify API (cicd.logrite.ai)
▲
│ Bearer COOLIFY_API_TOKEN
│
AI Service (aiservice.logrite.ai, port 9300)
│
│ 1. GET /api/v1/deployments/signal ← cheap, cached 5s server-side
│ 2. GET /api/v1/deployments ← expensive, only while active
▼
Browser (app.logrite.ai — DeploymentLockOverlay.jsx)
Frontend phaseEndpointCadenceCoolify calls per request
idle/api/v1/deployments/signalevery 5 min1 (+ shared 5s cache)
active/api/v1/deployments/signal + /api/v1/deployments5 min signal, 2 s detail1 + 26
cooldownnone (local 1-minute timer)—0

Trade-off: 5-minute idle cadence is deliberately slow to keep idle traffic near zero (~12 calls/hr/user). Short-lived deploys that start and finish between polls may not surface the overlay. For near-instant reaction without polling, migrate to Coolify outbound webhooks → SSE (see Future direction below).

The /signal endpoint responds in ~0.5 s and returns:

{
"active": true,
"count": 1,
"activeServices": ["analyzer-dev"],
"fetchedAt": "2026-04-21T05:19:18.538Z"
}

When active=false, the overlay stays dormant. When active=true and activeServices intersects the gated set, the overlay mounts and switches to fast-poll mode for per-service percent + stage enrichment.

  • Full-screen backdrop (fixed inset-0 z-[9999]) with blur — covers sidebar, content, everything.
  • Body scroll locked via document.body.style.overflow = 'hidden' while visible.
  • Keyboard blocked — Tab, Escape, Enter all preventDefault-ed on the overlay.
  • Per-service progress bars update every 2 s: display name, commit SHA (7-char), percent, stage text (e.g., “Building image”, “Deploying container”).
  • Cooldown — when all gated deploys clear, overlay shows a MM:SS countdown from 1:00 before unlocking. A new deploy during cooldown re-opens the active state.
ScenarioBehavior
Signal endpoint 500s or times outOverlay keeps last known state; retries on the next poll tick. Won’t falsely mount.
Detail endpoint fails mid-activeOverlay stays up with stale percent/stage until next successful detail poll.
AI Service container itself redeploysSignal calls fail during the swap window; frontend can’t detect a self-referential ai-service deploy. Accepted limitation.
Frontend container itself redeploysOld frontend served until new one swaps in — old bundle can’t lock for its own deploy. Accepted limitation.
Coolify returns unmapped application_id/signal falls back to empty activeServices; overlay stays down. Extend coolify-apps.ts with the new UUID to enable locking.
  • Backend: logrite-ai-service/src/routes/deployments.routes.ts (signal endpoint + cache)
  • Backend: logrite-ai-service/src/services/coolify.service.ts (Coolify client)
  • Backend config: logrite-ai-service/src/config/coolify-apps.ts (service-key → UUID map)
  • Frontend hook: Logrite-UI/src/components/DeploymentLock/useDeploymentLock.js
  • Frontend UI: Logrite-UI/src/components/DeploymentLock/DeploymentLockOverlay.jsx
  • Mount point: Logrite-UI/src/App.jsx (mounted inside <Router>, sibling to <AppContent>)

To gate another service: add its key to the GATED_SERVICES Set in useDeploymentLock.js. Make sure the key also exists in coolify-apps.ts.

To change the cooldown: adjust COOLDOWN_MS (default 60 * 1000) in useDeploymentLock.js.

To change the idle poll cadence: adjust SIGNAL_INTERVAL_MS (default 5 * 60 * 1000) in useDeploymentLock.js.

To tune the signal cache: adjust SIGNAL_CACHE_MS (default 5000) in deployments.routes.ts.

Future direction: Move from polling to Coolify outbound webhooks → AI Service state → SSE push to browsers. That eliminates all idle polling. Not required at current scale; noted for future iteration.