--- name: uncloud description: Use when managing an Uncloud cluster — deploying services, configuring Caddy ingress, adding static proxy routes for non-cluster devices, publishing ports, scaling, inspecting logs, or managing machines and volumes with the `uc` CLI. origin: ECC --- # Uncloud Cluster Management Reference for the `uc` CLI — a decentralised self-hosting platform using Docker containers, WireGuard mesh networking, and Caddy reverse proxy. ## When to Activate Use this skill when working with Uncloud clusters, especially when: - Bootstrapping or joining machines with `uc machine` - Deploying services from Compose files with `uc deploy` - Publishing HTTP, HTTPS, TCP, or UDP ports through Uncloud - Configuring Caddy ingress with `x-caddy`, `x-ports`, or `--caddyfile` - Routing external LAN devices through the cluster proxy - Inspecting logs, service state, volumes, DNS, or machine placement ## How It Works Uncloud runs Docker services across peer machines connected by a WireGuard mesh. Each machine is an equal cluster member; services communicate on the overlay network and Caddy runs globally to terminate public HTTP/HTTPS traffic. Compose files can use Uncloud extensions for ingress, placement, and generated Caddy configuration, while the `uc` CLI handles image distribution, scheduling, scaling, logs, and cluster state. ## Examples ```bash uc machine init user@host --name machine-1 uc service run --name web -p app.example.com:8080/https nginx:latest uc deploy ``` ## Core Concepts - **No central control plane** — all machines are equal peers connected by WireGuard - **Caddy** runs as a global service on every machine; auto-obtains TLS from Let's Encrypt - **Overlay network** — services communicate via `10.210.0.0/16` by default; DNS provided inside the mesh - **Caddyfile is autogenerated** — never edit it directly; use `x-caddy` / `--caddyfile` instead --- ## CLI Quick Reference ### Machines | Command | Purpose | |---------|---------| | `uc machine init user@host` | Bootstrap first machine / new cluster | | `uc machine add user@host` | Join machine to existing cluster | | `uc machine ls` | List machines | | `uc machine update NAME --public-ip IP` | Update public IP for ingress | | `uc machine rm NAME` | Remove machine | Key `init` flags: `--name`, `--network 10.210.0.0/16`, `--no-caddy`, `--no-dns`, `--public-ip auto\|IP\|none` ### Services | Command | Purpose | |---------|---------| | `uc service ls` / `uc ls` | List services | | `uc service run IMAGE` | Run a single container service | | `uc deploy` | Deploy from `compose.yaml` | | `uc deploy --no-build` | Deploy already-pushed images without rebuilding | | `uc deploy --recreate` | Force service recreation | | `uc scale SERVICE N` | Set replica count | | `uc service logs SERVICE` | View logs | | `uc service exec SERVICE` | Shell into container | | `uc service inspect SERVICE` | Detailed info | | `uc service rm SERVICE` | Remove service (keeps named volumes) | | `uc ps` | All containers across cluster | ### Images ```bash uc image push myapp:latest # Push local image to all machines uc image push myapp:latest -m machine1,machine2 # Push to specific machines uc images # List images in cluster ``` ### Volumes ```bash uc volume ls # All volumes uc volume ls -m machine1 # On specific machine uc volume create NAME -m MACHINE uc volume rm NAME ``` ### Caddy ```bash uc caddy config # Show current generated Caddyfile (read-only) uc caddy deploy # Deploy/upgrade Caddy across cluster ``` ### DNS & Context ```bash uc dns show # Show reserved *.uncld.dev domain uc dns reserve # Reserve a new domain uc ctx ls # List cluster contexts uc ctx use prod # Switch context ``` --- ## Port Publishing ### HTTP/HTTPS (via Caddy reverse proxy) ``` -p [hostname:]container_port[/protocol] ``` | Example | Meaning | |---------|---------| | `-p 8080/https` | HTTPS with auto `service-name.cluster-domain` hostname | | `-p app.example.com:8080/https` | HTTPS with custom hostname | | `-p 8080/http` | HTTP only, no TLS | ### TCP/UDP (host-bound, bypasses Caddy) ``` -p [host_ip:]host_port:container_port[/protocol]@host ``` | Example | Meaning | |---------|---------| | `-p 5432:5432@host` | TCP 5432 on all interfaces | | `-p 127.0.0.1:5432:5432@host` | TCP 5432 loopback only | | `-p 53:5353/udp@host` | UDP | --- ## Compose File Extensions Uncloud adds these extensions on top of Docker Compose: ### `x-ports` — publish ports with domains ```yaml services: app: image: app:latest x-ports: - example.com:8000/https - www.example.com:8000/https - api.example.com:9000/https ``` ### `x-caddy` — custom Caddy config for service ```yaml services: app: image: app:latest x-caddy: | example.com { redir https://www.example.com{uri} permanent } www.example.com { reverse_proxy {{upstreams 8000}} { import common_proxy } basic_auth /admin/* { admin $2a$14$... } } ``` Template functions available inside `x-caddy`: - `{{upstreams [service] [port]}}` — healthy container IPs - `{{.Name}}` — service name - `{{.Upstreams}}` — map of all services → IPs ### `x-machines` — placement constraints ```yaml services: db: image: postgres:18 x-machines: db-machine # Single machine name app: image: app:latest x-machines: - machine-1 - machine-2 ``` ### Full multi-service example ```yaml services: api: build: ./api x-ports: - api.example.com:3000/https environment: DATABASE_URL: postgres://db:5432/mydb web: build: ./web x-ports: - example.com:8000/https - www.example.com:8000/https environment: API_URL: http://api:3000 db: image: postgres:18 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - db-data:/var/lib/postgresql/data x-machines: db-machine volumes: db-data: ``` --- ## Routing to External (Non-Cluster) Devices To expose an external device (e.g. BMC, NAS, router UI) via Caddy without running a real container: **1. Create a Caddyfile snippet** (e.g. `~/device.caddyfile`): ```caddyfile https://device.example.com { reverse_proxy https://192.168.1.x { transport http { tls_insecure_skip_verify # needed for self-signed BMC certs } } log } ``` For plaintext upstream: `reverse_proxy http://192.168.1.x:port` **2. Register as a named service with no-op container:** ```bash uc service run \ --name device-bmc \ --caddyfile ~/device.caddyfile \ registry.k8s.io/pause:3.9 ``` `pause` is a minimal no-op container — it does nothing, but gives Uncloud a service entry to attach the Caddyfile to. **3. Verify:** ```bash uc caddy config # device.example.com block should appear ``` > `--caddyfile` cannot be combined with non-`@host` published ports. **DNS tip:** A wildcard record (`*.yourdomain.com → cluster-public-ip`) means any new subdomain works immediately — no DNS change needed per service. --- ## Service DNS (Internal) Services inside the cluster resolve each other by name: | DNS name | Resolves to | |----------|------------| | `service-name` | Any healthy container | | `service-name.internal` | Same | | `rr.service-name.internal` | Round-robin | | `nearest.service-name.internal` | Machine-local first | --- ## Scaling & Global Services ```bash uc scale web 5 # 5 replicas (spread across machines) uc scale web 1 # Scale down ``` ```yaml services: caddy: deploy: mode: global # One container on every machine ``` --- ## Image Tag Templates (in compose.yaml) ```yaml image: myapp:{{gitdate "20060102"}}.{{gitsha 7}} image: myapp:{{gitsha 7}}.${GITHUB_RUN_ID:-local} ``` | Function | Output | |----------|--------| | `{{gitsha N}}` | First N chars of commit SHA | | `{{gitdate "format"}}` | Git commit date in Go format | | `{{date "format"}}` | Current date | --- ## Common Workflows **Deploy from source:** ```bash uc deploy # Build + push + deploy uc build --push && uc deploy --no-build # Separate steps ``` **Inspect a service:** ```bash uc inspect web uc logs -f web uc logs --since 1h web uc exec web # Opens shell uc exec web /bin/sh -c "env" # Run specific command ``` **Zero-downtime deploys** happen automatically; Uncloud waits for health checks before terminating old containers. **Force recreate:** ```bash uc deploy --recreate ``` --- ## Common Mistakes | Mistake | Fix | |---------|-----| | Editing the Caddyfile directly | Use `x-caddy` in compose or `--caddyfile` on `uc service run` | | Proxying an HTTPS upstream with self-signed cert | Add `transport http { tls_insecure_skip_verify }` | | `uc caddy config` shows no user-defined blocks | Caddy admin socket unreachable — check `uc inspect caddy` and `uc logs caddy` | | Service can't reach external LAN IP from container | Verify Caddy container's host can route to target network | | Volumes lost after `uc service rm` | Named volumes persist; only anonymous volumes are auto-removed |