uncloud
affaan-m/ecc
Manage decentralised self-hosted Docker clusters with WireGuard mesh, Caddy ingress, and peer-based orchestration.
What is uncloud?
Uncloud is a decentralised self-hosting platform that runs Docker services across peer machines connected by WireGuard mesh networking. Use this skill when bootstrapping clusters, deploying services from Compose files, configuring Caddy reverse proxy ingress, publishing ports, scaling services, or managing machines and volumes via the `uc` CLI.
- Bootstrap and join machines to form a peer-to-peer cluster with no central control plane
- Deploy Docker services from Compose files with automatic image distribution and scheduling
- Publish HTTP/HTTPS ports through Caddy with auto-generated TLS certificates and custom domains
- Configure Caddy ingress rules, basic auth, and redirects via `x-caddy` and `x-ports` extensions
- Route external LAN devices (BMC, NAS, routers) through the cluster proxy without running containers
- Scale services, inspect logs, manage volumes, and view cluster state across all machines
How to install uncloud
npx skills add null --skill uncloud- Docker installed on each machine
- SSH access to machines being added to the cluster
- A public IP or domain for Caddy ingress (optional for internal-only clusters)
- Compose file with services to deploy (optional; can use `uc service run` for single containers)
How to use uncloud
- 1.Initialize the first machine with `uc machine init user@host --name machine-1` to bootstrap the cluster
- 2.Add additional machines with `uc machine add user@host` to join the cluster
- 3.Define services in a `compose.yaml` file using standard Docker Compose syntax plus Uncloud extensions (`x-ports`, `x-caddy`, `x-machines`)
- 4.Deploy services with `uc deploy` to build images, distribute them, and schedule containers across machines
- 5.Publish ports using `-p hostname:port/protocol` for HTTP/HTTPS (Caddy) or `-p host_port:container_port@host` for TCP/UDP
- 6.Scale services with `uc scale SERVICE N` and inspect logs with `uc service logs SERVICE`
- 7.View cluster state with `uc machine ls`, `uc service ls`, `uc volume ls`, and `uc caddy config`
Use cases
- Deploy a multi-service web application (API, frontend, database) across multiple machines with automatic HTTPS
- Expose a BMC or NAS management interface through Caddy with TLS termination and authentication
- Scale a stateless service to multiple replicas spread across cluster machines
- Set up DNS-based service discovery and internal routing between containerised services
- Manage machine lifecycle: bootstrap first node, add peers, update public IPs, and remove machines
- DevOps engineers managing self-hosted infrastructure
- Developers deploying multi-machine applications without Kubernetes
- System administrators running decentralised clusters on heterogeneous hardware
- Teams needing peer-based orchestration without a central control plane
uncloud FAQ
Services communicate over a WireGuard mesh on the `10.210.0.0/16` overlay network by default. Services resolve each other by name (e.g., `service-name` or `service-name.internal`) without needing to know IP addresses.
No. The Caddyfile is auto-generated and read-only. Use `x-caddy` in your Compose file or `--caddyfile` flag with `uc service run` to define custom Caddy configuration.
Create a Caddyfile snippet with a reverse proxy block, then register it as a named service using `uc service run --name device-name --caddyfile path/to/caddyfile registry.k8s.io/pause:3.9`.
Yes. Use the `x-machines` extension in your Compose file to pin services to named machines, or use `deploy.mode: global` to run a service on every machine.
Caddy automatically obtains and renews TLS certificates from Let's Encrypt for all published HTTPS domains. Ensure your cluster has a public IP and DNS is configured correctly.
Full instructions (SKILL.md)
Source of truth, from affaan-m/ecc.
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.
metadata:
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
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/16by default; DNS provided inside the mesh - Caddyfile is autogenerated — never edit it directly; use
x-caddy/--caddyfileinstead
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
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
uc volume ls # All volumes
uc volume ls -m machine1 # On specific machine
uc volume create NAME -m MACHINE
uc volume rm NAME
Caddy
uc caddy config # Show current generated Caddyfile (read-only)
uc caddy deploy # Deploy/upgrade Caddy across cluster
DNS & Context
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
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
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
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
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):
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:
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:
uc caddy config # device.example.com block should appear
--caddyfilecannot be combined with non-@hostpublished 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
uc scale web 5 # 5 replicas (spread across machines)
uc scale web 1 # Scale down
services:
caddy:
deploy:
mode: global # One container on every machine
Image Tag Templates (in compose.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:
uc deploy # Build + push + deploy
uc build --push && uc deploy --no-build # Separate steps
Inspect a service:
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:
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 |
Related skills
More from affaan-m/ecc and the wider catalog.
unified-memory
Agent skill from affaan-m/ecc.
unified-notifications-ops
Consolidate GitHub, Linear, and desktop alerts into one operator-driven notification workflow with severity routing and deduplication.
uspto-database
Official USPTO patent and trademark record lookup with reproducible research logs.
verification-loop
Comprehensive verification system for Claude Code sessions covering builds, types, lints, tests, and security.
video-editing
AI-assisted editing pipeline for cutting, structuring, and augmenting real video footage.
videodb
Ingest, index, search, and edit video and audio with real-time desktop capture and live stream monitoring.