Soong is a CLI tool for managing Lambda Labs GPU instances with automatic cost controls and idle detection. It provides one-command instance launching, real-time status monitoring, automatic shutdown of idle instances, and SSH tunnel management.
Key value: Multi-layer cost protection prevents runaway expenses from forgotten GPU instances ($0.50-$2.00/hour).
Local Machine (User)
└─ CLI Tool (Python/Typer/Rich) → Lambda Labs API
↓
GPU Instance (Lambda Labs)
├─ Status Daemon (Flask :8080) ← lifecycle/idle management
├─ SGLang Server (:8000) - LLM inference
├─ n8n Workflows (:5678) - automation
└─ Persistent Filesystem (/lambda/nfs/coding-stack/)
Cloudflare Worker (Optional)
├─ KV Storage (instance events)
├─ /event endpoint (POST) - Receive lifecycle events
├─ /events endpoint (GET) - Query event history
└─ /health endpoint (GET) - Health check
| Path | Purpose |
|---|---|
cli/src/soong/ |
Main CLI application code |
cli/src/soong/cli.py |
Entry point, all commands |
cli/src/soong/lambda_api.py |
Lambda Labs API client |
cli/src/soong/instance.py |
Instance lifecycle management |
cli/src/soong/config.py |
Configuration schema/validation |
cli/src/soong/models.py |
AI model registry, GPU recommendations |
cli/src/soong/ssh.py |
SSH tunnel management |
ansible/ |
Instance provisioning playbooks |
ansible/roles/status-daemon/templates/status_daemon.py.j2 |
Status daemon (server component) |
worker/ |
Cloudflare Worker watchdog |
docs/architecture/ |
System design documentation |
| Term | Definition |
|---|---|
| Lease Duration | Time commitment in hours (1-8) when launching an instance |
| IDLE_TIMEOUT_MINUTES | Minutes without activity before auto-shutdown (default: 30) |
| LAST_ACTIVITY | Timestamp of last detected activity (SSH, manual signal) |
| Status Token | Shared secret for authenticating daemon endpoints (32-byte URL-safe) |
| Hard Timeout | Absolute 8-hour limit from creation, no overrides |
| Watchdog | Cloudflare Worker that monitors health externally |
| SGLang | LLM inference server running on the GPU instance |
| n8n | Workflow automation platform running on the instance |
- Layer 1: Idle Detection (30 min) - No activity → auto-shutdown
- Layer 2: Lease System (4hr default, max 8hr) - Hard deadline
- Layer 3: Hard Timeout (8hr absolute) - No exceptions
- Layer 4: Cloudflare Watchdog - External failsafe
Location: ansible/roles/status-daemon/templates/status_daemon.py.j2
Flask server running on GPU instances at port 8080.
| Endpoint | Method | Auth | Purpose | Updates LAST_ACTIVITY? |
|---|---|---|---|---|
/health |
GET | No | Health check | No |
/status |
GET | Yes | Instance status JSON | No |
/activity |
POST | Yes | Signal activity manually | Yes |
/extend |
POST | Yes | Extend lease deadline | No |
/shutdown |
POST | Yes | Admin termination | No |
/ |
GET | Yes | HTML dashboard | No |
Runs every 60 seconds, checks:
- Active connections to SGLang API (port 8000) → reset LAST_ACTIVITY
- Lease expired → terminate
- Idle > 30 min → terminate
Activity resets LAST_ACTIVITY when:
- Active TCP connections to SGLang API (port 8000) detected via
ss - Manual POST to
/activityendpoint
The idle checker runs every 60 seconds and checks for established connections to the SGLang port. If clients are connected, the idle timer resets.
soong configure # Interactive setup wizard
soong start # Launch GPU instance
soong status # Show instance status and costs
soong status --history # View global instance history (from Worker)
soong ssh # Interactive SSH session
soong tunnel # Start SSH tunnels to services
soong extend <hrs> # Extend lease duration
soong stop # Terminate instance
soong worker deploy # Deploy Cloudflare Worker
soong worker status # Check Worker health
soong worker logs # Stream Worker logs
soong worker destroy # Destroy Worker and KVLocated at ~/.config/gpu-dashboard/config.yaml:
lambda:
api_key: string # Lambda API key
default_region: string # e.g., "us-west-1"
filesystem_name: string # e.g., "coding-stack"
status_daemon:
token: string # Shared secret
port: int # Default: 8080
cloudflare:
api_token: string # Cloudflare API token
account_id: string # Cloudflare account ID
kv_namespace_id: string # KV namespace (auto-created)
worker_url: string # Worker URL (auto-set on deploy)
defaults:
model: string # Default model ID
gpu: string # Default GPU type
lease_hours: int # Default: 4The optional Cloudflare Worker provides centralized event logging and instance history tracking.
Event Flow:
CLI → Worker /event endpoint → KV Storage
↓
Event Stream (SSE)
Event Types:
instance.created- Instance launchedinstance.terminated- Instance stoppedinstance.extended- Lease extendedinstance.failed- Launch/operation failed
Hard Fail (instance launch):
- If Worker is configured but unreachable, launch aborts
- Prevents untracked instances that could incur costs
Soft Fail (instance termination):
- Termination always succeeds, even if Worker is down
- Failed events queued in
~/.config/gpu-dashboard/pending_events.json - Automatically retried on next successful Worker contact
When Worker is offline, events are:
- Stored locally in
pending_events.json - Retried on next Worker operation (max 3 attempts)
- Include original timestamp for accurate history
- Events stored in Cloudflare KV with 30-day TTL
- Each event keyed by
event:{timestamp}:{instance_id} - Instance IDs indexed for efficient queries
cd cli
pip install -e ".[test]"
soong configurecd cli
pytest # Run all tests
pytest -v # Verbose
pytest tests/test_cli.py # Specific file- CLI calls Lambda API to launch instance
- Cloud-init runs on instance boot
- Ansible provisions services (SGLang, n8n, status-daemon)
- Status daemon starts background idle_checker thread
- Instance signals ready via health endpoint
The status daemon template uses these Ansible variables:
{{ lambda_api_key }}- API key for termination calls{{ status_token }}- Bearer token for auth{{ idle_timeout_minutes }}- Idle threshold (default: 30){{ lease_hours }}- Initial lease duration{{ max_lease_hours }}- Maximum lease (default: 8){{ sglang_port }}- SGLang API port (default: 8000){{ status_daemon_port }}- Daemon port (default: 8080)