Skip to content

ADR 0004 — Local Docker scaler + Kubernetes HPA

  • Status: Accepted
  • Date: 2026-04-26
  • Deciders: André Luiz Gallon

Context

Operators need to change the running agent count by moving a slider on /agents. There are two deployment modes:

  1. Docker Compose on a single developer workstation.
  2. Kubernetes in production, scaling 1 → 300.

We did not want the same code path to control both, because the abstractions are very different (docker compose --scale agent=N vs. kubectl scale deployment/web-agent --replicas=N).

Decision

  • Docker Compose: ship an opt-in but on-by-default in .env.example override (docker-compose.scaler.yml) that mounts /var/run/docker.sock into the dashboard container. The dashboard talks to the Docker Engine API to reconcile the agent service. Activated via COMPOSE_FILE=docker-compose.yml:docker-compose.scaler.yml.
  • Kubernetes: ship a HorizontalPodAutoscaler with minReplicas: 1, maxReplicas: 300 on CPU + memory in k8s/20-agent-deployment.yaml. The dashboard does not call the Kubernetes API directly; it persists the desired count to cluster_config.desired_agent_count, and operators wire the HPA metrics or use kubectl scale for manual overrides.
  • Both modes are documented in the README under Scaling the fleet → Local and Production deployment on Kubernetes.

Consequences

  • ✅ Operators get a real one-click scaler in the local dev story.
  • ✅ Production stays on the well-known HPA path; nothing weird happens at the cluster level.
  • ⚠️ The local scaler grants the dashboard FULL control of the host Docker daemon. Documented loudly in the override and disabled by default on shared hosts.
  • ⚠️ In Linux the dashboard must join the host's docker group; scripts/init-env.sh detects the right DOCKER_GID.

Alternatives considered

  • Single code path that uses Kubernetes API everywhere — rejected because requiring a kubelet on developer workstations defeats the point of the docker-compose path.
  • External scripts/scale-local.sh — kept as a fallback for users that don't want the socket mount, but the embedded scaler is the default for ergonomics.