When running containerized workloads, every engineering team eventually faces the scaling dilemma:
-
Vanilla Docker / Docker Compose is lightweight, fast, and wonderfully simple to maintainβbut it has zero native autoscaling. If your API traffic triples during a flash sale or your AI inference queue spikes, you must manually run
docker compose up --scale api=5. -
Kubernetes (K8s) provides Horizontal Pod Autoscaler (HPA)βbut it introduces an overwhelming operational tax:
metrics-server, complex CRDs, etcd clusters, steep learning curves, and hundreds of megabytes of baseline overhead per node.
What if you could keep the pure simplicity of standard docker-compose.yml files, but gain true horizontal autoscaling across multi-node clusters based on real-time CPU and NVIDIA GPU utilization?
That is exactly why we built Gubernator (gbnt)βthe "Goldilocks" container orchestrator that combines the dead-simple developer experience of Docker Swarm with the flexibility of Nomad and the enterprise governance of high-end platforms.
ποΈ The Architecture: How Gubernator Autoscaling Works
In Gubernator, nodes are called Centurions (Managers and Workers), and multi-container applications are deployed as Legions (Docker Compose stacks).
Under the hood, Gubernator's declarative autoscaling engine operates as an autonomous feedback loop:
ββββββββββββββββββββββββββββββββββββββββββ
β Prometheus Metrics Collector β
β (cAdvisor CPU % + NVIDIA DCGM GPU %) β
βββββββββββββββββββββ¬βββββββββββββββββββββ
β Telemetry Polling (10s)
βΌ
ββββββββββββββββββββββββββββββββββββββββββ
β Gubernator Autoscaler Engine Core β
β - Parses 'gbnt.autoscaling.*' labels β
β - Evaluates Target vs Current Metric β
β - Applies Cooldown & Damping Windows β
βββββββββββββββββββββ¬βββββββββββββββββββββ
β Desired Replicas (Β±Ξ)
βΌ
ββββββββββββββββββββββββββββββββββββββββββ
β Centurion Scheduler β
β (Spread across nodes / GPU Affinity) β
βββββββββββββββββββββ¬βββββββββββββββββββββ
β Docker Engine API
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Centurion Host 01 Centurion Host 02 (GPU) β
β [api-task-1] [api-task-2] [api-task-3] [llm-task] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Every 10 to 15 seconds, the Gubernator Watchdog invokes autoscaler.EvaluateAndAutoscale():
- Telemetry Ingestion: Queries Prometheus / cAdvisor for real-time container CPU percentages and NVIDIA DCGM exporter for GPU compute load.
- Evaluation: Averages utilization across all healthy running task replicas for that service.
- Threshold Calculation: $$\text{Desired Replicas} = \left\lceil \text{Current Replicas} \times \left( \frac{\text{Current Metric Value}}{\text{Target Threshold}} \right) \right\rceil$$
-
Guardrails & Cooldown: Clamps desired replicas within
[min, max]boundaries and verifies that thecooldownwindow (e.g. 60 seconds) has elapsed since the last scale event, preventing erratic flapping or thrashing. -
Intelligent Node Placement: Uses Gubernator's hardware scheduler (
SpreadorBinpack) to assign new container instances to the least-loaded Centurion hostβwith automatic hardware affinity targeting nodes with physical NVIDIA GPUs when GPU metrics are configured.
β‘ Declarative Autoscaling via Compose Labels
You don't need proprietary manifests or extra YAML definitions. You declare autoscaling policies directly inside your standard docker-compose.yml service definition using the gbnt.autoscaling.* label prefix:
1. High-Traffic Web Service (CPU-Based Scaling)
version: "3.8"
services:
api:
image: mycompany/fastapi-gateway:latest
ports:
- "8000:8000"
deploy:
replicas: 2
labels:
# Enable horizontal autoscaling
- "gbnt.autoscaling.enable=true"
# Scale based on CPU utilization
- "gbnt.autoscaling.metric=cpu"
# Target: scale up when average CPU exceeds 70%
- "gbnt.autoscaling.target=70"
# Boundary constraints
- "gbnt.autoscaling.min=2"
- "gbnt.autoscaling.max=10"
# 60-second cooldown between scale events
- "gbnt.autoscaling.cooldown=60"
# Spread tasks across all cluster Centurions
- "gbnt.autoscaling.scope=cluster"
- "gbnt.autoscaling.strategy=spread"
2. AI Inference / LLM Service (NVIDIA GPU-Based Scaling)
For heavy AI and machine learning workloads (e.g., vLLM, Ollama, Hugging Face TGI), CPU load is often misleading because the compute bottleneck lives inside the GPU VRAM and Tensor Cores.
Gubernator natively tracks NVIDIA DCGM GPU utilization:
version: "3.8"
services:
vllm-inference:
image: vllm/vllm-openai:latest
deploy:
replicas: 1
labels:
- "gbnt.autoscaling.enable=true"
# Target GPU Tensor Core load
- "gbnt.autoscaling.metric=gpu"
- "gbnt.autoscaling.target=80"
- "gbnt.autoscaling.min=1"
- "gbnt.autoscaling.max=4"
- "gbnt.autoscaling.cooldown=120"
# Target only Centurion nodes with GPU hardware
- "gbnt.autoscaling.scope=cluster"
placement:
constraints:
- "node.labels.gbnt.node.gpu == nvidia"
When load drops below the target threshold, Gubernator smoothly de-provisions excess containers in reverse order, ensuring zero data loss and respecting container graceful shutdown timeouts (SIGTERM followed by grace period).
π₯οΈ Full UI Control: Interactive Flutter Web Dashboard
Not a fan of editing raw YAML on the fly? Gubernator includes a full-screen Material Design 3 Dashboard written in Flutter Web:
-
Interactive
AUTOSCALEBadges: In both the Legions (Stacks) overview and the Containers (Tasks) table, every service displays a live clickable chip:- β‘
GPU β’ Cluster(Glowing amber/purple) - β‘
CPU β’ Cluster(Electric cyan) - π€
Off(Muted grey)
- β‘
-
Autoscale Control Dialog: Clicking any chip opens a visual control modal where operators can:
- Toggle autoscaling ON/OFF in 1 click
- Switch metrics between CPU and GPU
- Adjust the target percentage slider
- Set minimum and maximum replica limits
- Configure cooldown intervals
- Choose cluster-wide vs single-host pinning
- Audit Trail & SIEM: Every automatic scaling event is cryptographically sealed into Gubernator's immutable Forensic Audit Log (ENS op.mon.1) and forwarded to your enterprise SIEM (Splunk, Wazuh, Elastic) in CEF, RFC 5424 Syslog, or JSON.
π Quickstart: Try It Yourself in 60 Seconds
Installing Gubernator takes a single binary or automated installer:
# Download the latest binary for your architecture (Linux AMD64/ARM64, macOS)
curl -sSL https://github.com/mario-ezquerro/gubernator/releases/latest/download/gbnt-linux-amd64 -o gbnt
chmod +x gbnt
sudo mv gbnt /usr/local/bin/
# Initialize the manager node and monitoring stack
gbnt legion init
gbnt monitor init
Open your browser at http://localhost:4001 (Dashboard) and http://localhost:4002/swagger/index.html (REST API). Deploy your first compose stack and watch Gubernator seamlessly scale your tasks as traffic surges!
π Wrapping Up
Container orchestration doesn't have to be a choice between the primitive limitations of a single Docker daemon and the crushing complexity of Kubernetes.
With Gubernator, you get:
- Native Docker Compose compatibility
- Real-time CPU & NVIDIA GPU Horizontal Autoscaling
- Zero-CGO, single-binary Go engine
- Built-in Caddy Ingress, CoreDNS, GlusterFS storage, and OpenTelemetry SRE stack
- Enterprise Active Directory/LDAP, RBAC, and Spanish ENS forensic security
Give the project a star on GitHub and let us know your thoughts in the comments below!
π GitHub Repository: https://github.com/mario-ezquerro/gubernator
Top comments (0)