Basic Usage Guide¶
This guide explains how to use MANA day to day once it is installed and your pipeline is sending webhooks: how to see what it did, how to run health checks and rollbacks by hand, and how to read its logs.
kubectl -n orchestrator port-forward svc/orchestrator-service 8000:8000 &
export MANA=http://localhost:8000
In-cluster, use http://orchestrator-service.orchestrator.svc:8000.
How MANA Works¶
MANA is event-driven, not a monitoring loop:
- Receive. Your pipeline posts a webhook when a deployment starts, succeeds or fails.
- Record. On success, the version is stored as the rollback target. On failure, the failure count for that deployment ID is incremented.
- Decide. Retry (while the count is within
MAX_RETRIES), roll back (once it exceeds the limits and a previous version exists), or alert. - Act. A retry asks GitHub to re-run the workflow after a backoff. A rollback patches the Deployment back to the last good image tag.
- Verify. A rollback is only reported successful after all replicas are ready and each pod's health endpoint answers.
- Log. Every step is written to the audit log and counted in Prometheus metrics.
MANA only acts on deployments named in webhooks. It does not discover or watch Deployments by namespace or label.
Seeing What MANA Did¶
Look up one deployment¶
State is stored per deployment ID (the ID your pipeline puts in the webhook), for 24 hours:
curl -s $MANA/deployment/<deployment-id> | jq
{
"deployment_id": "deploy-123",
"namespace": "production",
"app_name": "sample-app",
"version": "v1a2b3c4-1760000000",
"status": "rolled_back",
"retry_count": 4,
"failure_type": "health_check_failure",
"previous_version": "v0f9e8d7-1759990000",
"timestamp": 1760000123.45,
"metadata": { "workflow_run_id": "9876543210" }
}
A 404 means the ID is unknown or older than 24 hours.
List recent deployments¶
curl -s "$MANA/deployments/recent" | jq # last 10
curl -s "$MANA/deployments/recent?namespace=production&limit=20" | jq
Results are newest first and include deployment_id, namespace, app_name, version, status, retry_count and timestamp.
Deployment statuses¶
| Status | Meaning |
|---|---|
retry_scheduled |
A workflow re-run is queued after the backoff |
rolling_back |
A rollback is in progress |
rolled_back |
The Deployment was patched back to the previous version and is healthy |
failed |
Recovery stopped: no workflow_run_id, no previous version, or the rollback was not healthy. Needs a human |
success |
The pipeline reported success |
pending, in_progress |
Defined but not currently written by the webhook handler |
Statuses are lowercase in the API.
Check the live Deployment in Kubernetes¶
For replica counts and conditions, ask Kubernetes directly:
kubectl -n production get deployment sample-app
kubectl -n production rollout history deployment/sample-app
kubectl -n production get pods -l app=sample-app -o wide
Note
GET /metrics/deployment/{namespace}/{name} is documented in the API Reference but is currently shadowed by the Prometheus endpoint, so use kubectl for this.
Manual Operations¶
Run a health check¶
Runs the same verification MANA uses after a rollback (all replicas ready, then each pod's /health). It blocks until the check passes, fails or times out.
curl -s -X POST $MANA/health-check -H 'Content-Type: application/json' \
-d '{"namespace":"production","deployment_name":"sample-app","timeout":60}' | jq
{
"deployment_name": "sample-app",
"namespace": "production",
"healthy": true,
"check_duration_seconds": 3.4
}
Roll back to a specific version¶
curl -s -X POST $MANA/rollback -H 'Content-Type: application/json' \
-d '{"namespace":"production","deployment_name":"sample-app","target_version":"v0f9e8d7-1759990000"}' | jq
{
"status": "success",
"deployment_id": "manual-rollback-6c1f0c0e-6f58-4a8b-9d5f-0d7a3e1b2c44",
"message": "Rolled back to version v0f9e8d7-1759990000"
}
The image tag of the application container (named like the Deployment, or the only container) is replaced, the version labels are updated, and health is verified. If the rollback is not healthy the call returns HTTP 500. Manual rollbacks are audited and counted (rollbacks_total{reason="manual"}) but do not create a stored deployment record. Use this when you want MANA's verification and audit trail instead of a plain kubectl rollout undo.
Simulate a pipeline event¶
Useful for testing and demos (see the Quick Start):
curl -s -X POST $MANA/webhook/deployment -H 'Content-Type: application/json' -d '{
"deployment_id": "test-1", "namespace": "staging", "app_name": "sample-app",
"version": "1.2.3", "status": "failed", "failure_type": "health_check_failure",
"metadata": {"workflow_run_id": "1"}
}' | jq
Warning
Webhooks have real effects: enough failures for the same ID will patch the live Deployment. Test in a non-production namespace.
Things there is no endpoint for¶
- Marking a deployment successful. Send a
successwebhook instead; it also records the version as the rollback target. - Triggering a retry directly. Retries are triggered by failure webhooks. To re-run a pipeline by hand, use GitHub (Actions, Re-run jobs).
- Listing monitored namespaces. There is no such concept; MANA acts on what the webhooks name.
Understanding Health Checks¶
Verification has two layers:
- Kubernetes readiness.
ready_replicasmust equal the Deployment'sreplicas. Until then MANA keeps waiting (polling every 10 s). - Application health. MANA calls
GET http://<pod-ip>:<HEALTH_CHECK_PORT><HEALTH_CHECK_PATH>on each running pod. A pod is healthy if it returns HTTP 200 with JSON containing"status": "healthy". The Deployment passes if at least one pod is healthy.
If replicas are ready but the application check fails 3 consecutive times, the verification fails early. Otherwise it gives up after 300 s (or the timeout you pass to POST /health-check). Details: Data Flow.
Test your application's endpoint the same way MANA does:
POD_IP=$(kubectl -n production get pod -l app=sample-app -o jsonpath='{.items[0].status.podIP}')
kubectl -n orchestrator exec deploy/orchestrator -- \
python -c "import requests; r=requests.get('http://$POD_IP:5000/health', timeout=5); print(r.status_code, r.text)"
Retries and Backoff¶
The delay before retry n is min(INITIAL_BACKOFF × BACKOFF_MULTIPLIER^(n-1), MAX_BACKOFF). With the defaults (10 s, ×2, cap 300 s):
| Retry | Delay |
|---|---|
| 1 | 10 s |
| 2 | 20 s |
| 3 | 40 s |
| 4 | 80 s |
| 5 | 160 s |
| 6 and later | 300 s (cap) |
There is no jitter. With the default MAX_RETRIES=3, only the first three delays are used before a rollback becomes possible. Tune with Orchestrator Configuration.
Reading the Audit Log¶
Audit events go to the orchestrator's standard output.
kubectl -n orchestrator logs -l app=orchestrator --tail=200
kubectl -n orchestrator logs -l app=orchestrator --since=1h
kubectl -n orchestrator logs -l app=orchestrator -f
# Only audit lines
kubectl -n orchestrator logs -l app=orchestrator | grep "AUDIT:"
# Everything about one deployment
kubectl -n orchestrator logs -l app=orchestrator | grep "<deployment-id>"
With the default log format, an audit line looks like:
2026-02-05 14:30:00,123 - self-healing-orchestrator.audit - INFO - AUDIT: rollback_completed
The structured fields (deployment ID, namespace, version, success, details) are attached to the log record as audit_event. To see them as JSON in your log pipeline, use a JSON log formatter; see Monitoring.
Audit actions¶
| Action | Meaning |
|---|---|
deployment_received |
A webhook arrived |
failure_detected |
A failure was recorded |
retry_initiated |
A retry was scheduled |
rollback_initiated |
A rollback started |
rollback_completed |
A rollback finished healthy |
rollback_failed |
A rollback did not become healthy |
health_check_started / health_check_passed / health_check_failed |
Health verification lifecycle |
state_saved |
State written to Redis |
manual_intervention_required |
Automatic recovery is exhausted; a person must act |
Inspecting Redis¶
kubectl -n orchestrator exec deploy/redis -- redis-cli keys 'deployment:*'
kubectl -n orchestrator exec deploy/redis -- redis-cli get 'last_successful:production:sample-app'
kubectl -n orchestrator exec deploy/redis -- redis-cli ttl 'deployment:<deployment-id>'
last_successful:<namespace>:<app> is the rollback target; it has no expiry. If a rollback keeps answering "No previous version", this key is probably missing.
Best Practices¶
- Send a success webhook for every good deployment so a rollback target always exists.
- Keep deployment IDs stable across retries of the same logical deployment, or the failure count resets (CI/CD Integration).
- Monitor MANA itself. Alert on
OrchestratorDown, pod restarts, and/healthreportingdegraded. - Watch the key metrics:
deployments_total{status="failed"},rollbacks_total,retries_total. See Monitoring. - Alert on
manual_intervention_requiredin your log pipeline; it means automation gave up. - Rehearse failures regularly in staging with the failure recipes.
- Review the audit trail periodically for recurring failure patterns.
- Restrict access to the API; it has no authentication (Security).
Troubleshooting Usage Issues¶
| Symptom | Checks |
|---|---|
| MANA does not seem to react to a deployment | Is the pipeline actually posting webhooks? kubectl logs ... \| grep webhook. Is ORCHESTRATOR_URL reachable from the runner? |
GET /deployment/<id> returns 404 |
The ID is wrong, the state expired (24 h), or the webhook was in_progress/success only (these do not store state) |
Responses say alert with "missing workflow_run_id" |
The failure webhook has no metadata.workflow_run_id; add it so a retry can be scheduled |
| Retries are scheduled but nothing re-runs | GITHUB_TOKEN or GITHUB_REPO is missing or invalid, or the run was still active; check logs for Failed to trigger re-run |
| Rollback says "No previous version" | No success webhook was ever recorded and pods have no version label |
| Rollback is unhealthy | Wrong HEALTH_CHECK_PORT/PATH, image pull problems, or a NetworkPolicy blocking the orchestrator; test the endpoint as shown above |
More in the Troubleshooting Guide.