Make the `DockerRuntimeOrchestrator` fully functional: apply container configs (memory, CPU, ports, env vars) when starting containers, generate correct Traefik routing labels, support replicas, implement blue/green and rolling deployment strategies, and monitor container health via Docker event stream.
## Scope
- Docker single-host only (Swarm and K8s are future `RuntimeOrchestrator` implementations)
- Replicas managed by the orchestrator as independent containers
- Traefik integration for path-based and subdomain-based routing
- Docker event stream for infrastructure-level health monitoring
- UI changes for new config fields, replica management, and deployment progress
The `cameleer-traefik` network has ICC enabled (required for Traefik routing and agent-server communication). All app containers are technically reachable from each other on this network. The security boundary is at the application level (auth tokens, environment-specific credentials).
The `cameleer-env-{slug}` networks provide **intentional service discovery isolation** — apps only discover and communicate with services in their own environment via Docker DNS. Cross-environment communication requires knowing the target container's IP, which apps have no reason to discover.
> **Future option:** Per-environment Traefik networks (each env gets its own network with Traefik and server attached) would provide full network-level isolation. This can be added based on customer security requirements without changing the orchestrator interface.
Wraps Docker network operations. `ensureNetwork(name)` creates a bridge network if it doesn't exist (idempotent). `connectContainer(containerId, networkName)` attaches a container to a second network. Called by `DeploymentExecutor` before container creation.
Pure function: `resolve(globalDefaults, envConfig, appConfig) → ResolvedContainerConfig`
`ResolvedContainerConfig` is a typed Java record with all fields resolved to concrete values. No more scattered `@Value` fields in `DeploymentExecutor` for container-level settings.
If false, Traefik passes through TLS to the container (requires the app to terminate TLS itself).
### Replicas
All replicas of the same app get identical Traefik labels. Traefik automatically load-balances across containers with the same service name on the same network.
---
## Deployment Status Model
### New fields on `deployments` table
| Column | Type | Description |
|--------|------|-------------|
| `target_state` | varchar | `RUNNING` or `STOPPED` |
| `deployment_strategy` | varchar | `BLUE_GREEN` or `ROLLING` |
`on-failure` with max 3 retries. Docker handles transient failures. After 3 retries exhausted, the container stays dead and `DockerEventMonitor` detects the permanent failure.
### Environment variables injected
Base env vars (always set):
```
CAMELEER_EXPORT_TYPE=HTTP
CAMELEER_APPLICATION_ID={appSlug}
CAMELEER_ENVIRONMENT_ID={envSlug}
CAMELEER_DISPLAY_NAME={containerName}
CAMELEER_SERVER_URL={resolvedServerUrl}
CAMELEER_AUTH_TOKEN={bootstrapToken}
```
Plus all entries from `customEnvVars` in the resolved config.
---
## Docker Event Monitor
### DockerEventMonitor
`@Component` that starts a persistent Docker event stream on `@PostConstruct`.
- Listens for events: `die`, `oom`, `stop`, `start`
- On `die`/`oom`: looks up deployment by container ID, updates replica status to `DEAD`, recomputes deployment status (RUNNING → DEGRADED → FAILED)
- On `start`: updates replica status to `RUNNING` (handles Docker restart policy recoveries)
- Reconnects automatically if the stream drops
### Interaction with agent heartbeats
- Agent heartbeats: app-level health (is the Camel context running, are routes active)
- Docker events: infrastructure-level health (is the container alive, OOM, crash)
- Both feed into the same deployment status. Docker events are faster for container crashes. Agent heartbeats catch app-level hangs where the container is alive but the app is stuck.
---
## Database Migration
`V7__deployment_orchestration.sql`:
```sql
-- New status values and fields for deployments
ALTER TABLE deployments ADD COLUMN target_state VARCHAR(20) NOT NULL DEFAULT 'RUNNING';
ALTER TABLE deployments ADD COLUMN deployment_strategy VARCHAR(20) NOT NULL DEFAULT 'BLUE_GREEN';
ALTER TABLE deployments ADD COLUMN replica_states JSONB NOT NULL DEFAULT '[]';
ALTER TABLE deployments ADD COLUMN deploy_stage VARCHAR(30);
-- Backfill existing deployments
UPDATE deployments SET target_state = CASE
WHEN status = 'STOPPED' THEN 'STOPPED'
ELSE 'RUNNING'
END;
```
The `status` column remains but gains two new values: `DEGRADED` and `STOPPING`. The `DeploymentStatus` enum is updated to match.
---
## UI Changes
### Deployments tab — Overview
- **Replicas column** in deployments table: shows `{healthy}/{total}` (e.g., `2/3`)
- **Status badge** updated for new states: `DEGRADED` (warning color), `STOPPING` (auto color)
- **Deployment progress** shown when `deploy_stage` is not null — horizontal step indicator:
```
●━━━━●━━━━●━━━━○━━━━○━━━━○
Pre- Pull Start Health Swap
flight reps check traffic
```
Completed steps filled, current step highlighted, failed step red.
### Create App page — Resources tab
-`appPort` — number input (default 8080)
-`replicas` — number input (default 1)
-`deploymentStrategy` — select: Blue/Green, Rolling (default Blue/Green)
-`stripPathPrefix` — toggle (default true)
-`sslOffloading` — toggle (default true)
### Config tab — Resources sub-tab (app detail)
Same fields as create page, plus visible in read-only mode when not editing.