Skip to main content
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:
uc 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

1

Create compose.yaml

Define your services, networks, and volumes in a Compose file.
2

Run uc deploy

Deploy to your cluster with automatic image building and zero-downtime updates.
3

Confirm the plan

Review the deployment plan and approve changes.
4

Monitor deployment

Watch as services roll out with health monitoring.

Deployment process

When you run uc deploy, Uncloud:
  1. Builds images for services with a build section using your local Docker
  2. Pushes built images directly to cluster machines (only missing layers are transferred)
  3. Plans the deployment showing what will change and asks for confirmation
  4. Creates missing volumes on target machines
  5. Deploys services using rolling updates with health monitoring

Compose file format

Uncloud supports the standard Compose Specification with some limitations and extensions.

Basic example

compose.yaml
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:
uc deploy

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:
compose.yaml
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

compose.yaml
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:
uc deploy --no-build

Recreate containers

Force recreation even if nothing changed:
uc deploy --recreate

Skip health monitoring

For faster emergency deployments:
uc deploy --skip-health
--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:
uc deploy web api

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:
.env
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
Add health checks for reliable deployments:
healthcheck:
  test: curl -f http://localhost/health || exit 1
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 30s
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"]
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

Build docs developers (and LLMs) love