Orchestrator Configuration¶
The orchestrator is configured entirely through environment variables, read once at startup by orchestrator/src/config.py. This page explains what each setting does, how the settings interact, and how to set them in each deployment method. For a flat list of every variable see Environment Variables; for the cross-component defaults table see the Configuration Reference.
Source of truth
This page documents the variables that the code actually reads. The broader Configuration Guide describes some additional options (leader election, webhook authentication, Redis TLS, and so on) that are not implemented in the current code. If a variable is not listed here or in Environment Variables, the orchestrator ignores it.
How Configuration Is Loaded¶
get_config()builds anOrchestratorConfigwithOrchestratorConfig.from_env()the first time it is called and caches it.validate()checks the values and raisesValueErroron the first violation.SelfHealingOrchestrator.__init__validates again, connects to Redis (PING) and loads the Kubernetes config. An invalid value raisesValueError; an unreachable Redis or an unloadable Kubernetes config raisesRuntimeError. In every case the process exits, so Kubernetes restarts the pod until the problem is fixed.
Changing a variable requires restarting the orchestrator pods. GET /config shows the values currently in effect (see the API Reference).
Settings¶
Redis¶
| Variable | Default | Description |
|---|---|---|
REDIS_HOST |
localhost |
Hostname of the Redis server. In-cluster this is redis-service |
REDIS_PORT |
6379 |
Redis port |
The client uses a 5-second connect and socket timeout. There is no password or TLS option in the current code, so use a network-isolated Redis (see Security).
Retry and rollback policy¶
| Variable | Default | Constraint | Description |
|---|---|---|---|
MAX_RETRIES |
3 |
>= 1 |
Number of failures answered with a retry; the next failure rolls back |
INITIAL_BACKOFF |
10.0 |
> 0 |
Seconds to wait before the first retry |
BACKOFF_MULTIPLIER |
2.0 |
> 1.0 |
Growth factor between retries |
MAX_BACKOFF |
300.0 |
> INITIAL_BACKOFF |
Upper bound on any single delay, in seconds |
The delay before retry n is:
delay(n) = min(INITIAL_BACKOFF × BACKOFF_MULTIPLIER^(n-1), MAX_BACKOFF)
| Retry | Defaults (10 / 2 / 300) | Aggressive (2 / 2 / 30) | Conservative (30 / 3 / 600) |
|---|---|---|---|
| 1 | 10 s | 2 s | 30 s |
| 2 | 20 s | 4 s | 90 s |
| 3 | 40 s | 8 s | 270 s |
| 4 | 80 s | 16 s | 600 s (capped) |
Choosing values. The decision rule is retry while failure_count <= MAX_RETRIES, otherwise roll back if a previous version exists and the failed version is the one running (see Data Flow):
MAX_RETRIES=3(default): three retries, rollback on the fourth failure.MAX_RETRIES=1: one retry, rollback on the second failure. Good for fast-moving staging environments.MAX_RETRIES=0is rejected by validation, so at least one retry is always attempted before a rollback. There is currently no "roll back immediately" mode.ROLLBACK_THRESHOLDwas removed. If it is still set, the orchestrator logs a deprecation warning and ignores it.
The worst-case time spent retrying before a rollback is the sum of the backoff delays plus the time each re-run takes. With the defaults that is 70 s of backoff alone. If your target MTTR is under a minute, lower INITIAL_BACKOFF and MAX_RETRIES.
Health checks¶
| Variable | Default | Constraint | Description |
|---|---|---|---|
HEALTH_CHECK_TIMEOUT |
5 |
> 0 |
Seconds to wait for each HTTP request to a pod's health endpoint |
HEALTH_CHECK_PORT |
5000 |
1 to 65535 |
Port the application listens on inside its pod |
HEALTH_CHECK_PATH |
/health |
none | Path of the application's health endpoint |
The application health check is only the second half of verification. First, Kubernetes must report ready_replicas == replicas. The orchestrator then calls http://<pod-ip>:<HEALTH_CHECK_PORT><HEALTH_CHECK_PATH> on every running pod and expects HTTP 200 with a JSON body containing "status": "healthy".
Two related values are not environment variables:
- The overall verification timeout is 300 s by default (
check_deployment_health(timeout=300)); the manualPOST /health-checkendpoint accepts atimeoutfield. - The number of consecutive application-health failures tolerated before giving up is fixed at 3 (
max_app_health_failures).
If your application returns a different health payload, either make it return {"status": "healthy"} or adapt check_application_health().
GitHub integration¶
| Variable | Default | Description |
|---|---|---|
GITHUB_TOKEN |
unset | Token used to call the workflow re-run API |
GITHUB_REPO |
unset | Repository in owner/repo form |
Both are read directly by the orchestrator (not through OrchestratorConfig). If either is missing the orchestrator starts, logs a warning, and any attempted retry fails with an error log. GET / and GET /health report github_integration: false in that case.
The token needs permission to re-run workflows: Actions: Read and write on a fine-grained token scoped to the repository, or the repo (and workflow) scopes on a classic token. Store it in a Kubernetes Secret, never in a ConfigMap or image:
kubectl create secret generic github-token \
--from-literal=token="$GITHUB_TOKEN" \
-n orchestrator
orchestrator/k8s/deployment.yaml already reads it as secretKeyRef: {name: github-token, key: token}.
Logging and audit¶
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
INFO |
Intended application log level |
ENABLE_AUDIT_LOGGING |
true |
Intended switch for audit logging |
Parsed but not yet enforced
Both values are loaded and validated into the config object, but the logging setup in orchestrator.py and api.py uses a fixed INFO level, and the audit logger is always on. Changing these variables currently has no effect on output.
Service¶
| Variable | Default | Description |
|---|---|---|
PORT |
8000 |
Listen port when starting with python -m src.api. The container CMD uses uvicorn --port 8000 directly, so PORT does not affect the container |
Setting Values by Deployment Method¶
cd orchestrator
cp .env.example .env # edit values, never commit .env
set -a; source .env; set +a
uvicorn src.api:app --reload --port 8000
Set values under services.orchestrator.environment in docker-compose.yml. The shipped file sets REDIS_HOST=redis, MAX_RETRIES=3 and LOG_LEVEL=INFO. Add GITHUB_TOKEN and GITHUB_REPO there (or via an env_file) to enable retries.
Edit the env: list in orchestrator/k8s/deployment.yaml, then:
kubectl apply -f orchestrator/k8s/ -n orchestrator
kubectl rollout restart deployment/orchestrator -n orchestrator
Remember to change GITHUB_REPO (marked # REPLACE THIS) to your repository.
The orchestrator module exposes max_retries (plus replicas, resources and Redis options). The Deployment it creates sets only PORT, REDIS_HOST, REDIS_PORT, MAX_RETRIES and LOG_LEVEL. It does not set GITHUB_TOKEN or GITHUB_REPO, so a Terraform-deployed orchestrator has no GitHub integration (retries cannot trigger re-runs) until you add them. See Configuration Reference.
Presets¶
Copy-paste starting points. Adjust to your risk tolerance.
# Development: fast feedback
MAX_RETRIES=2
INITIAL_BACKOFF=2.0
MAX_BACKOFF=30.0
BACKOFF_MULTIPLIER=2.0
HEALTH_CHECK_TIMEOUT=3
# Production: patient, but bounded
MAX_RETRIES=3
INITIAL_BACKOFF=10.0
MAX_BACKOFF=120.0
BACKOFF_MULTIPLIER=2.0
HEALTH_CHECK_TIMEOUT=5
Validation Errors¶
validate() raises ValueError with one of these messages. The pod will crash-loop until the value is corrected; check kubectl logs -n orchestrator -l app=orchestrator.
| Message | Cause |
|---|---|
max_retries must be >= 1 |
MAX_RETRIES is 0 or negative |
initial_backoff must be > 0 |
INITIAL_BACKOFF is 0 or negative |
max_backoff must be > initial_backoff |
MAX_BACKOFF is less than or equal to INITIAL_BACKOFF |
backoff_multiplier must be > 1.0 |
BACKOFF_MULTIPLIER is 1.0 or less |
health_check_timeout must be > 0 |
HEALTH_CHECK_TIMEOUT is 0 or negative |
health_check_port must be between 1-65535 |
HEALTH_CHECK_PORT out of range |
Non-numeric values for numeric variables fail earlier with Python's own ValueError: invalid literal for int().
Resources and Permissions¶
| Setting | Value | Where |
|---|---|---|
| Replicas | 2 | orchestrator/k8s/deployment.yaml (the Terraform module's replicas variable also defaults to 2) |
| Requests / limits | 100m CPU, 256Mi / 500m CPU, 512Mi | orchestrator/k8s/deployment.yaml |
| Probes | GET /health on 8000, 60 s initial delay, 20 s period |
orchestrator/k8s/deployment.yaml |
| Security context | runAsNonRoot, UID 1000, no privilege escalation, all capabilities dropped |
orchestrator/k8s/deployment.yaml |
| RBAC | cluster-wide get/list/watch/update/patch on Deployments and ReplicaSets; get/list/watch on Pods; create/patch on Events |
orchestrator/k8s/deployment.yaml |
Running two replicas
Both replicas serve webhooks and share state through Redis. There is no leader election, so two replicas can both act on the same deployment ID if the CI system delivers a webhook twice. Use a single replica if you need strict single-actor behaviour.