Skip to content

Scaling Overview

Scale Mantis to handle large deployments with multiple Thorax instances, load balancing, and high availability.

Mantis is designed for horizontal scaling with multiple orchestration nodes:

ComponentScaling StrategyState Management
Mandible (API)Horizontal, statelessSession state in PostgreSQL
Thorax (Orchestration)Horizontal, coordinatedRedis cluster state
Lens (UI)Horizontal, statelessAPI-backed
Tarsus (Agent)Per-targetLocal state only

For development and small deployments:

  • Single Mandible + Thorax instance
  • Direct gRPC communication
  • No Redis or RabbitMQ required

For high availability and scale:

With queueing enabled, commands flow through RabbitMQ for distributed processing:

Agents maintain connections to specific Thorax instances:

Redis coordinates state across Thorax instances:

State TypeStoragePurpose
Instance registryRedis String (JSON) + Set indexTrack live instances
Target affinityRedis String (SETNX) + SetAgent→Thorax mapping
Deployment stateRedis String (JSON)Cross-instance progress
HeartbeatsRedis TTL keysInstance liveness

:::note Unvalidated starting points The tables in this section are rules of thumb for sizing a first deployment, not measured limits — Mantis ships no benchmark or constant behind these numbers. Treat them as starting points and adjust from your own metrics.

Two limits that do exist in the product, and are the real ceilings to watch:

  • database.max_connections (default 10) — the effective throughput ceiling for a single Mandible replica. Raise it (and PostgreSQL’s max_connections) before adding replicas if the DB pool is the bottleneck.
  • internal_grpc.max_connections (default 100) — the agent-registration ceiling per Mandible. A fleet larger than this needs the value raised or more Mandible replicas. :::
Deployment SizeMandibleThoraxDatabaseMemory/Instance
Small (<100 targets)112 vCPU, 4GB512MB
Medium (100-500)224 vCPU, 8GB1GB
Large (500-2000)3+3+8 vCPU, 16GB2GB
Enterprise (2000+)5+5+16+ vCPU, 32GB+4GB

Suggested (not shipped) starting thresholds — Mantis ships no alert rules, so these are examples to adapt, not defaults:

MetricSuggested thresholdAction
API latency p99>500msAdd Mandible instances
Deployment queue depth>100Add Thorax instances
Database connections>80% poolIncrease pool or instances
Target connections per Thorax>500Add Thorax instances
Component FailureImpactRecovery
Single MandibleNone (load balanced)Automatic
Single ThoraxTargets reassigned; in-flight deployments failed60s liveness, checked every 30s
RabbitMQFallback to pollingAutomatic
RedisCluster state lostRebuild on reconnect
PostgreSQL PrimaryRead-only until failoverDepends on HA setup
thorax.toml
[cluster]
enabled = true
instance_id = "thorax-1" # Unique per instance
redis_url = "redis://redis-cluster:6379"
heartbeat_interval_secs = 10
liveness_timeout_secs = 30
namespace = "thorax"
[queue]
enabled = true
host = "rabbitmq-cluster"
port = 5671
prefetch_count = 10
fallback_on_failure = true
VariablePurpose
THORAX__CLUSTER__ENABLEDEnable cluster mode
THORAX__CLUSTER__INSTANCE_IDUnique instance identifier (registry key)
THORAX__CLUSTER__REDIS_URLRedis connection string
THORAX__QUEUE__ENABLEDEnable RabbitMQ consumption
THORAX__QUEUE__HOSTRabbitMQ hostname

Both Thorax (:9090/metrics) and Mandible (:9091/metrics) expose Prometheus endpoints. These thresholds are example values, not shipped alert rules — author your own based on your baseline:

MetricSourceExample threshold
mantis_client_sessions_active{status="online"} (label key is status)Prometheus (Thorax /metrics)<2
mantis_command_queue_depthPrometheus>100
mantis_deployment_duration_secondsPrometheusp99 >5min
Terminal window
# Check Thorax cluster state. The registry is plain Redis keys (not Redis
# Cluster), so `--cluster info` reports nothing about Thorax. List the live
# instances from the registry index set instead:
redis-cli SMEMBERS mantis-prod:instances:index
# Check RabbitMQ consumers
rabbitmqctl list_consumers
# Check instance health (HTTP metrics/health server on port 9090)
curl -s http://thorax:9090/health
  1. Use rolling updates - Don’t restart all instances at once
  2. Monitor during scale events - Watch queue depth and latency
  3. Test failover - Regularly kill instances to verify HA
  4. Use anti-affinity - Spread instances across zones/nodes
  1. Unique instance IDs - Required for proper clustering
  2. Consistent namespaces - Use same namespace across cluster
  3. Appropriate prefetch - Balance throughput vs memory
  4. Enable fallback - RabbitMQ polling fallback for resilience
  1. Plan for burst - provision headroom above steady-state for deployment spikes (size it from your own peak, not a fixed multiple)
  2. Monitor database connections - Primary bottleneck
  3. Right-size Redis - Cluster state is small but critical
  4. Load test before production - Validate scaling assumptions