Open Source · Self-Hosted · Multi-Tenant

Deploy Docker Compose stacks
for multiple tenants

StackPilot is a self-hosted control plane for running tenant-isolated Docker Compose stacks across one or more Docker hosts — lightweight, auditable, and easy to operate.

Quick start
# Clone and configure
git clone https://gitlab.azuris.ch/azuris/stackpilot.git
cp .env.dev.example .env.dev

# Start local dev environment
make dev

# GUI → http://localhost:5173
# Hub → http://localhost:8113

The missing piece between
bare Docker and a full PaaS

Small teams, agencies, and labs often need to run workloads for multiple tenants on shared infrastructure. A full PaaS is too heavy. Bare Docker has no multi-tenancy. StackPilot fills that gap with a lightweight, auditable, policy-enforcing control plane.

Every Docker Compose file a tenant submits is parsed, rewritten through a security pipeline, and deployed under strict isolation rules — before it ever touches the daemon.

See all features →
3
Components
Hub · Agent · GUI
Hosts
Scale horizontally
mTLS
Security
Hub ↔ Agent
CI
Integration
Git push deploy

Everything you need to run a
multi-tenant Docker platform

StackPilot handles the hard parts — security policy, routing, certificates, storage — so you can focus on your tenants' workloads.

Compose Rewrite Pipeline

Every submitted compose file is parsed, filtered, and rewritten before deployment. Dangerous capabilities are stripped, resource limits enforced, and bind mounts sandboxed to tenant-owned directories.

Automatic HTTPS & Routing

Traefik integration with automatic Let's Encrypt certificates via ACME DNS challenges. HTTP and TCP routes per stack, with configurable entrypoints and cert resolvers.

Git-Driven Deployments

Tenants can deploy directly from a Git repository. The agent clones, reads the compose file, and deploys — with auto-sync on a configurable interval to keep stacks up to date.

Named Storage Locations

Define named storage locations (e.g. fast on SSD, large on HDD). Tenants reference them by name; the agent resolves them to host paths with security validation.

Storage Quotas

Per-tenant GB quotas per storage location, aggregated across all stacks on all hosts. Over-quota tenants are blocked from deploying new stacks with clear error reporting.

Multi-Tenant Accounts

Full tenant lifecycle management: create, configure, deactivate. Per-tenant resource limits, allowed domain lists, port allowances, and owner/member user accounts.

mTLS Agent Security

Hub-to-agent communication is secured with mutual TLS. The hub acts as its own CA, issuing per-host client certificates. Agents never expose themselves directly to tenants.

Mail Proxy

Optional mail routing profile using Dovecot and Exim4. Route IMAP, SMTP, and submission traffic to tenant mail containers via compose labels — no per-tenant mail config files needed.

Live Container Metrics

Real-time container stats — CPU %, memory usage, and network I/O — surfaced in the GUI per stack. Logs streaming with tail/since filters and per-service selection.

Three-tier architecture

A clean separation between the control plane, the execution layer, and the user interface.

🌐
Browser
React SPA (GUI)
HTTPS · Cookie + CSRF
🏛
Hub
FastAPI + PostgreSQL
  • Auth & sessions
  • Tenant & host management
  • Policy enforcement
  • Credential store
  • Agent proxy
HTTPS / mTLS · Admin API key
⚙️
Agent A
FastAPI + SQLite
  • Compose rewriting
  • Docker management
  • Cert watching
  • Git sync
···
⚙️
Agent B
FastAPI + SQLite
  • Compose rewriting
  • Docker management
  • Cert watching
  • Git sync
Hub is the single control plane. It owns users, tenants, host registrations, credentials, and routing metadata. It never exposes agents directly to tenant traffic.
Agents run on each Docker host, close to the daemon. They validate, rewrite, and deploy compose files. Agent state is intentionally host-local — SQLite, not a shared DB.
GUI talks to the hub only. It never contacts agents directly. All agent operations are proxied through the hub's API.

Quick Start

Get a local development environment running in under 5 minutes.

1

Clone the repository

git clone https://gitlab.azuris.ch/azuris/stackpilot.git
cd stackpilot
2

Configure the environment

cp .env.dev.example .env.dev
# Edit .env.dev to set your local secrets
3

Start everything

make dev
4

Open the GUI

1

Prepare your server

A Linux server with Docker and Docker Compose installed. Two servers are recommended: one for the hub, one (or more) for agents.

2

Deploy the hub

# On your hub server
cp docker-compose.hub.yml /opt/stackpilot/
# Set required environment variables:
# HUB_ADMIN_USERNAME, HUB_ADMIN_PASSWORD
# SECRET_ENCRYPTION_KEY (strong random key)
# ALLOWED_ORIGINS, DATABASE_URL
# HUB_DATA_DIR=/var/data/stackpilot/hub

docker compose -f docker-compose.hub.yml up -d
3

Deploy the agent

# On each agent host
cp docker-compose.agent.yml /opt/stackpilot/
# Set required environment variables:
# HOST_STACKS_DIR=/var/data/stackpilot/agent/stacks
# STORAGE_LOCATIONS=fast:/var/users/ssd,large:/var/users/hdd
# AGENT_DATA_DIR=/var/data/stackpilot/agent

docker compose -f docker-compose.agent.yml up -d
4

Register the host in the GUI

Log in as admin → Hosts → Add Host. Enter the agent URL and the admin API key. StackPilot will verify connectivity and generate mTLS certificates automatically.

Read docs/PRODUCTION.md for the full production checklist before exposing StackPilot to users.

Requirements

StackPilot runs wherever Docker runs.

🐳

Docker & Compose

Docker Engine 24+ and Docker Compose v2 on each host. The hub and agents are themselves Docker Compose stacks.

🐧

Linux Host

Any modern Linux distribution. Tested on Debian/Ubuntu and Alpine-based systems. macOS is supported for local development via OrbStack or Multipass.

🔐

TLS Certificate

A valid TLS certificate for the hub's public domain. Let's Encrypt works well. Agents use auto-generated mTLS certificates managed by the hub.

🌐

DNS

DNS-01 ACME challenge support recommended for automatic certificate issuance for tenant domains. A DNS API is needed (e.g. Gandi, Cloudflare).

Security by design

StackPilot enforces isolation at every layer of the deployment pipeline.

Compose rewriting — Every tenant compose file is parsed and rewritten before deployment. Dangerous keys (privileged, cap_add, pid, userns_mode, network_mode: host) are stripped or constrained.
Bind mount sandboxing — All bind mounts in tenant compose files are rewritten to paths under the tenant's own directory or explicitly allowed storage locations. Cross-tenant path traversal is impossible.
Resource limits — CPU and memory limits are enforced on every service. Tenants cannot exceed the limits configured by the operator, even if they try.
Network isolation — Each stack gets its own Docker network. External network access is restricted to an operator-controlled allowlist. Tenants cannot join arbitrary host networks.
Port allowances — No host ports are published by default. Operators explicitly grant per-tenant port ranges. Unallowed ports: entries are stripped with a warning.
gVisor support — Optional runsc runtime for additional container sandboxing on hosts where gVisor is installed.
CSRF protection — All mutating hub API calls require a CSRF token paired to the browser session. API keys bypass CSRF only for machine-to-machine access.
Docker socket proxying — Agents access Docker via tecnativa/docker-socket-proxy, exposing only the required API endpoints, not the raw socket.

Community & Contributing

StackPilot is an open-source project. Contributions, bug reports, and feedback are welcome.

Development setup

# Fork and clone
git clone https://gitlab.azuris.ch/azuris/stackpilot.git && cd stackpilot

# Install Python deps (agent + hub)
pip install -r requirements.txt -r hub/requirements.txt

# Install frontend deps
cd gui && npm install && cd ..

# Run the test suite
pytest tests/ hub_tests/ -q

# Run linting
ruff check agent hub && mypy agent hub