Deployment
This guide covers deploying PondPilot Proxy in production environments.
Architecture Requirements
Section titled “Architecture Requirements”PondPilot Proxy requires:
- Docker — To run user containers (SQLFlite instances)
- Docker socket access — The proxy manages containers via Docker API
- Two ports — HTTP (8080) and gRPC (8081)
- Network connectivity — To reach configured databases
Docker Compose Deployment
Section titled “Docker Compose Deployment”Basic Setup
Section titled “Basic Setup”services: proxy: image: ghcr.io/pondpilot/proxy:latest ports: - "8080:8080" # HTTP - "8081:8081" # gRPC (Flight SQL) environment: - JWT_SECRET=${JWT_SECRET} - CONTAINER_NETWORK=pondpilot volumes: - /var/run/docker.sock:/var/run/docker.sock - ./config.yaml:/config.yaml networks: - pondpilot restart: unless-stopped
networks: pondpilot: driver: bridgeWith Local Databases (Development)
Section titled “With Local Databases (Development)”services: proxy: image: ghcr.io/pondpilot/proxy:latest ports: - "8080:8080" - "8081:8081" environment: - JWT_SECRET=${JWT_SECRET} - CONTAINER_NETWORK=pondpilot - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/analytics volumes: - /var/run/docker.sock:/var/run/docker.sock - ./config.yaml:/config.yaml networks: - pondpilot depends_on: - postgres
postgres: image: postgres:16 environment: POSTGRES_PASSWORD: postgres POSTGRES_DB: analytics volumes: - postgres_data:/var/lib/postgresql/data networks: - pondpilot
networks: pondpilot: driver: bridge
volumes: postgres_data:Environment Variables
Section titled “Environment Variables”Required
Section titled “Required”# JWT signing secret (minimum 32 characters)JWT_SECRET="your-secret-key-at-least-32-characters-long"Optional
Section titled “Optional”# Server portsPORT=8080GRPC_PORT=8081
# Container settingsCONTAINER_IMAGE=ghcr.io/pondpilot/sqlflite:latestCONTAINER_IDLE_TIMEOUT=300000 # 5 minutes in msCONTAINER_MEMORY_LIMIT=512mCONTAINER_CPU_LIMIT=0.5CONTAINER_NETWORK=pondpilot
# AI integrationCLAUDE_API_KEY=sk-ant-...OPENAI_API_KEY=sk-...TLS Configuration
Section titled “TLS Configuration”Behind a Reverse Proxy (Recommended)
Section titled “Behind a Reverse Proxy (Recommended)”Use nginx, Traefik, or Caddy for TLS termination:
nginx example:
server { listen 443 ssl http2; server_name proxy.example.com;
ssl_certificate /etc/ssl/certs/proxy.crt; ssl_certificate_key /etc/ssl/private/proxy.key;
# HTTP endpoints location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}
# gRPC requires separate server blockserver { listen 443 ssl http2; server_name grpc.proxy.example.com;
ssl_certificate /etc/ssl/certs/proxy.crt; ssl_certificate_key /etc/ssl/private/proxy.key;
location / { grpc_pass grpc://localhost:8081; }}With Caddy (Automatic TLS)
Section titled “With Caddy (Automatic TLS)”proxy.example.com { reverse_proxy localhost:8080}
grpc.proxy.example.com { reverse_proxy localhost:8081 { transport http { versions h2c } }}Health Checks
Section titled “Health Checks”Configure health checks for orchestrators:
services: proxy: image: ghcr.io/pondpilot/proxy:latest healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 10sAvailable Endpoints
Section titled “Available Endpoints”| Endpoint | Purpose | Response |
|---|---|---|
GET /health | Basic health | {"status":"healthy"} |
GET /ready | Readiness probe | {"ready":true} |
GET /health/detailed | Component status | Detailed JSON |
Resource Planning
Section titled “Resource Planning”Memory Requirements
Section titled “Memory Requirements”| Component | Memory |
|---|---|
| Proxy service | ~100MB |
| Per user container | 512MB (configurable) |
| 10 concurrent users | ~5.1GB total |
| 50 concurrent users | ~25.5GB total |
CPU Requirements
Section titled “CPU Requirements”| Load | Recommended |
|---|---|
| Light (< 10 users) | 2 cores |
| Medium (10-50 users) | 4 cores |
| Heavy (50+ users) | 8+ cores |
Container Limits
Section titled “Container Limits”Configure based on query complexity:
containers: memory_limit: "512m" # Light queries memory_limit: "1g" # Medium queries memory_limit: "2g" # Heavy analytics cpu_limit: 0.5 # Light cpu_limit: 1.0 # Medium cpu_limit: 2.0 # HeavyScaling Considerations
Section titled “Scaling Considerations”Single Host Limits
Section titled “Single Host Limits”The current architecture runs containers on a single Docker host:
- Limited by host resources
- Maximum ~100 concurrent containers (configurable)
- Suitable for small-to-medium deployments
Horizontal Scaling (Future)
Section titled “Horizontal Scaling (Future)”The Orchestrator interface supports alternative backends:
- Kubernetes pods
- Fly.io machines
- AWS Fargate tasks
Contact us for enterprise scaling solutions.
Monitoring
Section titled “Monitoring”View proxy logs:
docker compose logs -f proxyKey log patterns:
level=info msg="Container spawned" user_id=abc123 container_id=def456level=info msg="Container stopped" user_id=abc123 reason=idle_timeoutlevel=warn msg="Rate limit exceeded" user_id=abc123level=error msg="Container spawn failed" user_id=abc123 error="..."Metrics
Section titled “Metrics”The proxy exposes basic metrics at /health/detailed:
{ "status": "healthy", "containers": { "active": 5, "total_spawned": 127, "total_stopped": 122 }, "uptime_seconds": 86400}Security Hardening
Section titled “Security Hardening”Docker Socket
Section titled “Docker Socket”The proxy requires Docker socket access. Mitigate risks:
- Run proxy as non-root where possible
- Use Docker socket proxy like Tecnativa/docker-socket-proxy
- Limit container capabilities (already enforced by proxy)
Network Isolation
Section titled “Network Isolation”User containers should only access:
- Configured databases
- The proxy (for gRPC communication)
networks: pondpilot: driver: bridge internal: false # Needs external access for databasesFor stricter isolation, use network policies or separate networks per security tier.
Secrets Management
Section titled “Secrets Management”Never commit secrets. Use:
- Environment variables from
.envfiles - Docker secrets
- Vault or similar secret management
services: proxy: environment: - JWT_SECRET=${JWT_SECRET} # From .env secrets: - jwt_secret # Or Docker secrets
secrets: jwt_secret: external: trueBackup and Recovery
Section titled “Backup and Recovery”Stateless Design
Section titled “Stateless Design”The proxy is stateless:
- No persistent data in the proxy itself
- User containers are ephemeral
- Database connections are configured via config
What to Back Up
Section titled “What to Back Up”- config.yaml — Your configuration
- Environment variables — Secrets and connection strings
- Source databases — Your actual data
Recovery
Section titled “Recovery”- Deploy fresh proxy instance
- Apply configuration
- Users reconnect automatically
Troubleshooting
Section titled “Troubleshooting”Container spawn failures
Section titled “Container spawn failures”# Check Docker socketls -la /var/run/docker.sock
# Check proxy logsdocker compose logs proxy | grep -i "spawn"
# Check Docker daemondocker infogRPC connection issues
Section titled “gRPC connection issues”# Test gRPC portnc -zv localhost 8081
# Check if gRPC server starteddocker compose logs proxy | grep -i "grpc"Database connection failures
Section titled “Database connection failures”# Test from hostpsql "${DATABASE_URL}"
# Test from container networkdocker run --rm --network pondpilot postgres:16 \ psql "${DATABASE_URL}" -c "SELECT 1"Memory issues
Section titled “Memory issues”If containers are being killed:
# Check container statsdocker stats
# Increase memory limit in configcontainers: memory_limit: "1g"