A complete SaaS management server built with Ctrl Plane and Forge. This single-file example demonstrates every major subsystem working together: multi-tenant management, instance lifecycle, deployments, health checks, event subscriptions, and custom platform routes.
Prerequisites
- Go 1.25+
- Docker (optional, for the Docker provider)
- A database if using a persistent store (PostgreSQL, MongoDB, or filesystem for Badger)
Quick Start
The fastest way to run the example uses the in-memory store (no dependencies):
go run ./examples/saas-platform
Open the interactive API docs at http://localhost:8080/docs.
Environment Variables
| Variable |
Default |
Description |
CP_STORE |
memory |
Store backend: memory, bun, badger, mongo |
CP_BASE_PATH |
/api/cp |
URL prefix for all Ctrl Plane routes |
CP_SEED |
true |
Create demo tenants on startup |
CP_DOCKER_HOST |
(auto) |
Docker daemon address |
CP_DOCKER_NETWORK |
bridge |
Docker network for containers |
CP_BUN_DRIVER |
postgres |
Bun driver: postgres or sqlite |
CP_BUN_DSN |
— |
Database connection string for Bun |
CP_BADGER_PATH |
./data/badger |
Directory for Badger data files |
CP_MONGO_URI |
mongodb://localhost:27017 |
MongoDB connection URI |
CP_MONGO_DATABASE |
ctrlplane |
MongoDB database name |
Copy .env.example to .env and adjust as needed.
Store Backends
Memory (default)
CP_STORE=memory go run ./examples/saas-platform
No setup required. Data is lost on restart.
PostgreSQL (via Bun)
CP_STORE=bun \
CP_BUN_DSN="postgres://user:pass@localhost:5432/ctrlplane?sslmode=disable" \
go run ./examples/saas-platform
SQLite (via Bun)
CP_STORE=bun \
CP_BUN_DRIVER=sqlite \
CP_BUN_DSN="file:ctrlplane.db?cache=shared&_pragma=journal_mode(WAL)" \
go run ./examples/saas-platform
Badger (embedded)
CP_STORE=badger \
CP_BADGER_PATH=./data/badger \
go run ./examples/saas-platform
MongoDB
CP_STORE=mongo \
CP_MONGO_URI="mongodb://localhost:27017" \
go run ./examples/saas-platform
In addition to the full Ctrl Plane API (under /api/cp), this example adds:
| Method |
Path |
Description |
GET |
/api/platform/status |
System stats, providers, uptime |
GET |
/api/platform/health |
Lightweight health probe |
POST |
/api/platform/webhooks/events |
Webhook receiver |
API Walkthrough
After starting the server, try these commands:
curl -s http://localhost:8080/api/platform/status | jq
List tenants (seeded)
curl -s http://localhost:8080/api/cp/v1/admin/tenants | jq
Create an instance
curl -s -X POST http://localhost:8080/api/cp/v1/instances \
-H "Content-Type: application/json" \
-d '{
"name": "my-app",
"provider": "docker",
"config": {
"image": "nginx:alpine",
"cpu_millis": 500,
"memory_mb": 256
}
}' | jq
List instances
curl -s http://localhost:8080/api/cp/v1/instances | jq
curl -s http://localhost:8080/api/platform/health | jq
Send a webhook
curl -s -X POST http://localhost:8080/api/platform/webhooks/events \
-H "Content-Type: application/json" \
-d '{
"source": "github",
"type": "push",
"payload": {"ref": "refs/heads/main"}
}' | jq
What This Example Demonstrates
- Store selection — switch between four store backends with an environment variable.
- Forge integration — OpenAPI docs, structured routing, extension lifecycle.
- Provider registration — Docker provider for container orchestration.
- Event subscriptions — real-time logging of instance, deploy, and health events.
- Custom routes — platform-specific endpoints alongside the Ctrl Plane API.
- Tenant seeding — pre-populated tenants with quotas for immediate exploration.
- NoopAuth — development-friendly auth that allows all operations.
Further Reading