Deploy complex multi-service applications to Uncloud using standard Docker Compose files. Uncloud supports most Compose features with some useful extensions for cluster deployments.
Quick start
Create a compose.yaml file and deploy:
This builds images (if needed), plans the deployment, and rolls out your services with zero downtime.
Using uc deploy
The uc deploy command reads your Compose file and deploys all defined services to your cluster.
Basic workflow
Create compose.yaml
Define your services, networks, and volumes in a Compose file.
Run uc deploy
Deploy to your cluster with automatic image building and zero-downtime updates.
Confirm the plan
Review the deployment plan and approve changes.
Monitor deployment
Watch as services roll out with health monitoring.
Deployment process
When you run uc deploy, Uncloud:
Builds images for services with a build section using your local Docker
Pushes built images directly to cluster machines (only missing layers are transferred)
Plans the deployment showing what will change and asks for confirmation
Creates missing volumes on target machines
Deploys services using rolling updates with health monitoring
Uncloud supports the standard Compose Specification with some limitations and extensions.
Basic example
services :
web :
image : nginx:alpine
x-ports :
- example.com:80/https
volumes :
- web-data:/usr/share/nginx/html
api :
build : ./api
x-ports :
- api.example.com:8000/https
environment :
DATABASE_URL : postgres://db:5432/myapp
REDIS_URL : redis://cache:6379
depends_on :
- db
- cache
db :
image : postgres:16
environment :
POSTGRES_PASSWORD : ${DB_PASSWORD}
volumes :
- postgres-data:/var/lib/postgresql/data
cache :
image : redis:alpine
volumes :
- redis-data:/data
volumes :
web-data :
postgres-data :
redis-data :
Deploy with:
Supported features
Uncloud implements most common Compose features:
Services
build: Build images from Dockerfile
image: Container image specification
command: Override default command
entrypoint: Override default entrypoint
environment: Environment variables
env_file: Load environment from file
volumes: Named volumes, bind mounts, tmpfs
healthcheck: Container health checks
cpus: CPU limits
mem_limit: Memory limits
user: Run as specific user
privileged: Extended privileges
sysctls: Kernel parameters
devices: Device access
gpus: GPU access
Deploy configuration
mode: replicated or global
replicas: Number of replicas
update_config: Rolling update settings
Volumes
Named Docker volumes
Bind mounts
Tmpfs mounts
Volume labels
External volumes
Configs
File-based configs
Inline configs
See the Compose support matrix for a complete feature list.
Uncloud-specific extensions
Uncloud adds custom extensions to enhance cluster deployments.
x-ports: Publishing service ports
Expose services via Caddy reverse proxy or bind to host ports:
services :
web :
image : nginx
x-ports :
# HTTPS via Caddy
- example.com:80/https
- www.example.com:80/https
# HTTP via Caddy
- api.example.com:8080/http
# TCP port bound to host
- 127.0.0.1:5432:5432/tcp@host
# UDP port on all interfaces
- 53:53/udp@host
Format:
HTTP/HTTPS (ingress): [hostname:]container_port[/protocol]
TCP/UDP (host): [host_ip:]host_port:container_port[/protocol]@host
x-caddy: Custom reverse proxy configuration
Provide custom Caddyfile configuration for advanced routing:
services :
app :
image : myapp:latest
x-caddy : |
www.example.com {
redir https://example.com{uri} permanent
}
example.com {
# Basic auth for admin section
basic_auth /admin/* {
admin $2a$14$... # bcrypt hash
}
# Cache static assets
header /static/* Cache-Control max-age=604800
# Reverse proxy to container port 8000
reverse_proxy {{upstreams 8000}} {
import common_proxy
}
log
}
You can also load from a file:
services :
app :
image : myapp:latest
x-caddy : ./Caddyfile
Template functions:
{{upstreams [port]}}: Space-separated list of healthy container IPs
{{upstreams "service" port}}: Upstreams for another service
{{.Name}}: Current service name
x-machines: Placement constraints
Restrict which machines can run your service:
services :
web :
image : nginx
scale : 3
x-machines :
- us-east-1
- us-east-2
- us-west-1
Uncloud automatically spreads replicas across the specified machines. For a single machine:
services :
db :
image : postgres:16
x-machines : db-server
Multi-service deployments
Real-world example
Here’s the actual compose.yaml from the Uncloud website deployment:
services :
uncloud-website :
image : ghcr.io/psviderski/uncloud-website
build :
cache_from :
- type=gha,scope=uncloud-website
cache_to :
- type=gha,mode=max,scope=uncloud-website
platforms :
- linux/amd64
- linux/arm64
user : nobody
x-caddy : |
uncloud.run {
redir /discord https://discord.gg/eR35KQJhPu
reverse_proxy {{upstreams 8000}} {
import common_proxy
}
log
}
scale : 2
x-machines :
- uc-prod-us2
- uc-prod-ap1
This deploys 2 replicas across 2 specific machines with custom Caddy configuration.
Complex application stack
services :
# Frontend
web :
build :
context : ./web
platforms :
- linux/amd64
- linux/arm64
x-ports :
- app.example.com:3000/https
environment :
API_URL : https://api.example.com
scale : 3
# Backend API
api :
build :
context : ./api
args :
BUILD_ENV : production
x-ports :
- api.example.com:8000/https
environment :
DATABASE_URL : postgres://postgres:${DB_PASSWORD}@db:5432/myapp
REDIS_URL : redis://cache:6379
SECRET_KEY : ${API_SECRET_KEY}
healthcheck :
test : curl -f http://localhost:8000/health
interval : 10s
retries : 3
start_period : 30s
scale : 3
depends_on :
- db
- cache
# Database
db :
image : postgres:16
environment :
POSTGRES_PASSWORD : ${DB_PASSWORD}
POSTGRES_DB : myapp
volumes :
- postgres-data:/var/lib/postgresql/data
x-machines : db-server
# Cache
cache :
image : redis:alpine
volumes :
- redis-data:/data
command : redis-server --appendonly yes
volumes :
postgres-data :
external : true
redis-data :
Deploy with environment variables:
export DB_PASSWORD = "secure-password"
export API_SECRET_KEY = "your-secret-key"
uc deploy
Deployment options
Build arguments
Pass build-time variables:
uc deploy --build-arg BUILD_ENV=production --build-arg VERSION= 1.0.0
Skip building
Deploy configuration changes without rebuilding:
Recreate containers
Force recreation even if nothing changed:
Skip health monitoring
For faster emergency deployments:
--skip-health won’t detect failing containers. Use only when confident about the deployment.
Auto-confirm in CI/CD
Skip confirmation prompt:
uc deploy --yes
# or
export UNCLOUD_AUTO_CONFIRM = true
uc deploy
Deploy specific services
Deploy only selected services and their dependencies:
Use different files
Specify custom Compose files:
uc deploy -f compose.yaml -f compose.prod.yaml
Enable profiles
Activate Compose profiles:
uc deploy --profile monitoring --profile logging
Service scaling
Set the number of replicas in your Compose file:
services :
web :
image : nginx
scale : 5 # Run 5 replicas
Or use the old deploy.replicas syntax:
services :
web :
image : nginx
deploy :
replicas : 5
Global services
Run one replica on every machine:
services :
monitoring-agent :
image : prometheus/node-exporter
deploy :
mode : global
Update configuration
Control rolling update behavior:
services :
web :
image : myapp:latest
deploy :
update_config :
# Stop old before starting new (prevents data corruption)
order : stop-first
# Wait 10s after container starts before checking health
monitor : 10s
See Rolling Updates for details.
Volume management
Named volumes
Create persistent volumes:
services :
db :
image : postgres:16
volumes :
- db-data:/var/lib/postgresql/data
volumes :
db-data :
driver : local
labels :
project : myapp
External volumes
Use pre-created volumes:
services :
app :
image : myapp
volumes :
- shared-data:/data
volumes :
shared-data :
external : true
Create the volume before deploying:
uc volume create shared-data
Bind mounts
Mount host directories:
services :
web :
image : nginx
volumes :
- /var/www/html:/usr/share/nginx/html:ro
Tmpfs mounts
In-memory filesystems:
services :
app :
image : myapp
volumes :
- type : tmpfs
target : /tmp
tmpfs :
size : 100M
Best practices
Store secrets and configuration in .env files: DB_PASSWORD = secret
API_KEY = your-key
Reference in Compose: environment :
POSTGRES_PASSWORD : ${DB_PASSWORD}
Use specific versions instead of latest: services :
db :
image : postgres:16.1 # Not postgres:latest
Optimize image size with multi-stage Dockerfiles: FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
CMD [ "node" , "dist/index.js" ]
Externalize persistent volumes
Mark persistent volumes as external to prevent accidental deletion: volumes :
postgres-data :
external : true
Troubleshooting
View deployment plan without deploying
Uncloud shows the plan before executing. Review it carefully before confirming.
Check service status
# List all services
uc ls
# Inspect specific service
uc inspect web
# View container logs
uc logs web
Deployment fails midway
Uncloud performs rolling updates one container at a time. If a deployment fails, successfully updated containers remain in the new state while others keep the old version.
Run uc deploy again to retry the failed containers.
Image build failures
Check build logs for errors. Common issues:
Missing dependencies in Dockerfile
Build context too large (use .dockerignore)
Insufficient memory/disk space
Next steps
Deploying Services Learn about uc run for single services
Rolling Updates Zero-downtime deployment strategies
Scaling Scale services across machines
Support Matrix Complete list of supported Compose features