Deploying a Docker Application with Claude Code and Smoooth
This guide walks through deploying a containerized application with persistent PostgreSQL and automated SSL routing on an unmanaged Linux VPS using Claude Code and Smoooth. By the end of this tutorial, Claude Code will configure, provision, and verify a running Docker workload over the Model Context Protocol (MCP) without root shell access or raw Docker socket exposure.
The Architecture Problem: Raw Docker Socket vs. Scoped MCP
Developers currently instruct AI coding agents to manage infrastructure by providing SSH credentials or mounting /var/run/docker.sock directly into containerized coding environments. While functional for basic tasks, giving an autonomous LLM unconstrained root-equivalent Docker daemon access introduces serious hazards:
- Root privilege escalation: Any process with Docker socket access can mount the host filesystem (
-v /:/host) and modify system files. - No rollback state machine: If an ad-hoc
docker runcommand fails halfway through, intermediate containers, orphan volumes, and colliding ports remain stranded. - Fragile secret management: Database passwords and API keys passed as CLI arguments leak into unencrypted shell history and host process lists (
ps aux).
Smoooth solves this by running as a lightweight Rust daemon (consuming under 28MB RAM idle) bound exclusively to the private host loopback interface (127.0.0.1:4040). Claude Code communicates via typed MCP tools guarded by 256-bit cryptographic bearer tokens.
Prerequisites
Before beginning, ensure you have the following:
- An unmanaged Linux VPS running Ubuntu 24.04 LTS or Debian 12 (1 vCPU, 1 GB RAM minimum).
- Docker Engine 24+ installed on the VPS (
docker --version). - Ports
80and443open in your VPS firewall for public web ingress. - Anthropic's Claude Code CLI installed on your workstation (
npm install -g @anthropic-ai/claude-code).
Step 1: Install Smoooth on Your VPS
SSH into your target VPS and install the single static Smoooth binary. The installer registers a minimal systemd service with NoNewPrivileges=true:
curl -fsSL https://smoooth.dev/install.sh | bash
Verify that the daemon is active and inspect its idle footprint:
smoooth doctor
Expected output:
[✓] System: Linux 6.8.0-45-generic (x86_64)
[✓] Memory RSS: 24.8 MB (limit: 512 MB)
[✓] Docker Socket: /var/run/docker.sock (connected, v26.1.3)
[✓] Ingress: Caddy v2.8.4 (ready on :80, :443)
[✓] Loopback API: http://127.0.0.1:4040/health (200 OK)
[✓] Security Envelope: sandboxed (NoNewPrivileges, ProtectSystem=strict)
Now, generate a scoped 256-bit bearer token for Claude Code. We grant plan and deploy capabilities with a 30-day expiration:
smoooth token create --scope plan,deploy --ttl 30d
Save the generated token (e.g., smt_live_8f39a0b4...). It will only be displayed once.
Step 2: Connect Claude Code to Smoooth over MCP
On your local development machine, configure Claude Code to register Smoooth as an active MCP server. You can connect securely over an SSH tunnel or directly via an HTTPS endpoint with bearer authentication:
Option A: Via Local SSH Forwarding (Recommended for homelabs and staging)
# Establish tunnel from local 4040 to VPS loopback 4040
ssh -N -L 4040:127.0.0.1:4040 root@your-vps-ip &
# Register Smoooth in Claude Code
claude mcp add --transport sse smoooth http://127.0.0.1:4040/sse \
--header "Authorization: Bearer smt_live_8f39a0b4..."
Option B: Via Stdio Over SSH (Zero Open Ports)
Claude Code can also invoke Smoooth directly over an SSH subprocess without exposing any HTTP port:
claude mcp add --transport stdio smoooth -- ssh -q root@your-vps-ip "smoooth mcp"
Open a new session in Claude Code and run /mcp to verify the connection. You will see Smoooth's typed tools registered:
list_capabilities: Discovers verified adapters and provider constraints.create_plan: Compiles and validates a 2-phase transactional DAG plan.execute_plan: Provisions containers, volumes, and networks with fencing leases.configure_ingress: Routes domains and provisions automatic TLS via Caddy.verify_connection: Runs live TCP/HTTP probes confirming workload health.
Step 3: Deploying the Container Stack
With MCP configured, instruct Claude Code to deploy your workload in natural language:
Deploy our container image ghcr.io/myorg/api:v1.2.0 to my VPS.
Mount a persistent PostgreSQL 16 database, configure health probes,
and route public traffic for api.example.com to port 3000 with automatic TLS.
What Claude Code Executes Under the Hood
Instead of guessing bash commands, Claude Code executes a verified sequence of typed MCP tool calls:
1. Tool Call: create_plan
{
"workspace_id": "production",
"spec": {
"workloads": [
{
"name": "api-service",
"image": "ghcr.io/myorg/api:v1.2.0",
"port": 3000,
"env": {
"NODE_ENV": "production",
"PORT": "3000"
},
"depends_on": ["pg-primary"]
}
],
"databases": [
{
"name": "pg-primary",
"provider": "postgres",
"version": "16-alpine",
"database_name": "app_db",
"persistent_volume": true
}
],
"ingress": {
"domain": "api.example.com",
"target_workload": "api-service",
"target_port": 3000,
"tls": true
}
}
}
Smoooth inspects the spec, checks host port availability, plans persistent storage under /var/lib/smoooth/volumes/production_pg_primary, and returns a verified plan with a canonical SHA-256 hash:
{
"plan_id": "plan_9f8a2b3c4d5e",
"canonical_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"risk_tier": "standard",
"actions_count": 4,
"estimated_ram_mb": 140,
"status": "ready"
}
2. Tool Call: execute_plan
Claude Code invokes execution with an idempotency key to prevent accidental double-provisioning:
{
"plan_id": "plan_9f8a2b3c4d5e",
"idempotency_key": "deploy_20261023_01"
}
Smoooth provisions the PostgreSQL container, connects the shared Docker bridge network, injects the dynamic connection string (DATABASE_URL) into the application container, boots the API container, and configures Caddy's dynamic JSON config.
3. Tool Call: verify_connection
Smoooth automatically executes active probes:
- TCP probe to PostgreSQL on port 5432 (passes in 12ms).
- HTTP probe to
http://127.0.0.1:<dynamic_port>/health(returns200 OK). - ACME certificate issuance confirmation from Let's Encrypt for
api.example.com.
Troubleshooting & Edge Cases
1. Transactional Rollback on Failed Health Checks
If your application crashes on startup (for example, missing a required runtime environment variable), Smoooth's 2-phase transactional DAG immediately triggers a rollback:
Automatic Rollback Guarantee: When health probes fail, Smoooth stops the newly launched container, restores previous network bindings, removes unverified Caddy routing rules, and preserves database volume state. Claude Code receives the exact container stderr trace to diagnose the issue immediately.
2. Handling Port Collisions
Traditional Docker scripts fail with bind: address already in use when port 3000 is occupied. Smoooth eliminates port collisions by decoupling internal container ports from host loopback bindings. Host ports are dynamically assigned from an ephemeral pool and mapped directly into Caddy's upstream proxy.
3. Permission Scopes
If Claude Code attempts an action exceeding its token scope (e.g. attempting to drop a volume with a read_only or plan token), Smoooth rejects the request with HTTP 403:
{
"error": "Forbidden",
"message": "Token scope 'plan' does not permit action 'execute_plan'. Required: 'deploy'",
"audit_event_id": "aud_7c29e1f4"
}
Comparison: Claude Code Deployment Approaches
| Criteria | Raw Docker Socket Mount | Dokku / Coolify SSH | Smoooth MCP |
|---|---|---|---|
| Protocol | Raw UNIX Socket | Raw Bash Shell (SSH) | Model Context Protocol (JSON-RPC) |
| Security Boundary | None (Host root access) | Full user shell permissions | 256-bit Scoped Tokens & systemd sandbox |
| Idle Memory Footprint | Host Docker overhead | 1.2 GB - 1.8 GB RAM (Coolify) | < 28 MB RAM (Rust binary) |
| Rollback Support | Manual bash cleanup | Basic container restart | 2-Phase Transactional DAG rollback |
| Database Persistence | Ad-hoc volume paths | Plugin-managed | Deterministic volume mounts & health probes |
For a deeper architectural breakdown of operational requirements and control plane designs, read our full Smoooth vs Coolify comparison.
Ready to give Claude Code infrastructure superpowers?
Deploy Smoooth on any Ubuntu or Debian VPS in under 30 seconds. Zero cloud lock-in, zero runtime bloat, and pure MCP control.