Skip to main content
Deploy individual containerized services to your Uncloud cluster using the uc run command. This guide covers running services from container images with various configuration options.

Quick start

Deploy a simple nginx web server:
uc run nginx
This pulls the nginx:latest image and starts one container on an available machine in your cluster.

Using uc run

The uc run command creates and starts a new service from a container image. Each service gets a unique ID and optional name.

Basic syntax

uc run [OPTIONS] IMAGE [COMMAND] [ARG...]
  • IMAGE: Container image to run (e.g., nginx, postgres:16, ghcr.io/user/app:v1.0)
  • COMMAND: Optional command to override the image’s default command
  • ARG: Arguments to pass to the command

Name your service

Give your service a custom name with --name:
uc run --name web nginx
Service names must be 1-63 characters, lowercase letters, numbers, and dashes only. They must start and end with a letter or number.

Publishing ports

Make your services accessible from outside the cluster by publishing ports.

HTTP/HTTPS ports (ingress mode)

Expose HTTP/HTTPS services via the Caddy reverse proxy:
# Publish port 8000 as HTTPS on app.example.com
uc run -p app.example.com:8000/https myapp:latest

# Publish port 80 as HTTP
uc run -p example.com:80/http nginx

# Multiple ports with different hostnames
uc run -p app.example.com:8000/https -p api.example.com:9000/https myapp:latest
Port format: [hostname:]container_port[/protocol]
  • hostname (optional): Domain name for accessing the service. If omitted and you have a cluster domain, uses <service-name>.<cluster-domain>
  • container_port: Port the container listens on
  • protocol (optional): http or https (default: https)
Caddy automatically obtains TLS certificates from Let’s Encrypt for HTTPS endpoints.

TCP/UDP ports (host mode)

Bind ports directly to the host machine for non-HTTP services:
# Bind PostgreSQL port to localhost only
uc run -p 127.0.0.1:5432:5432@host postgres:16

# Bind DNS port on all interfaces (UDP)
uc run -p 53:53/udp@host dnsmasq

# Custom host port
uc run -p 8080:80/tcp@host nginx
Port format: [host_ip:]host_port:container_port[/protocol]@host
  • host_ip (optional): IP address to bind to. If omitted, binds to all interfaces
  • host_port: Port on the host machine
  • container_port: Port inside the container
  • protocol (optional): tcp or udp (default: tcp)
Do not publish internal services like databases unless absolutely necessary. Services within the cluster can communicate using DNS names without publishing ports.

Environment variables

Pass configuration to your containers using environment variables.

Set individual variables

# Single variable
uc run -e POSTGRES_PASSWORD=secret postgres:16

# Multiple variables
uc run -e DB_HOST=postgres -e DB_PORT=5432 myapp:latest

Use environment files

Load variables from a file:
uc run --env-file .env myapp:latest
Example .env file:
.env
DATABASE_URL=postgres://user:pass@db:5432/mydb
REDIS_URL=redis://cache:6379
API_KEY=your-secret-key

Volume mounts

Persist data or share files between the host and containers.

Named volumes

Use Docker volumes for persistent storage:
# Mount volume 'db-data' to /var/lib/postgresql/data
uc run -v db-data:/var/lib/postgresql/data postgres:16

# Read-only volume
uc run -v config:/etc/app:ro myapp:latest
Named volumes must be created before running the service. They persist even after the container is removed.

Bind mounts

Mount a host directory into the container:
# Mount host path to container path
uc run -v /host/path:/container/path nginx

# Read-only bind mount
uc run -v /host/config:/etc/app:ro myapp:latest
Bind mounts are useful for sharing configuration files or data directories from the host.

Temporary filesystems

Create in-memory filesystems:
uc run --tmpfs /tmp myapp:latest

# With size limit (100MB)
uc run --tmpfs /tmp:size=100m myapp:latest
Tmpfs mounts are useful for temporary data that doesn’t need to persist.

Advanced options

Custom Caddy configuration

For advanced reverse proxy features, provide custom Caddyfile configuration:
uc run --caddyfile ./Caddyfile myapp:latest
Example Caddyfile:
Caddyfile
www.example.com {
    redir https://example.com{uri} permanent
}

example.com {
    basic_auth /admin/* {
        admin $2a$14$...  # bcrypt hash
    }

    reverse_proxy {{upstreams 8000}} {
        import common_proxy
    }
    log
}
The {{upstreams port}} template function automatically lists healthy container IPs.

Resource limits

Limit CPU and memory usage:
# Limit to 2 CPUs and 1GB memory
uc run --cpus 2 --memory 1g myapp:latest

# Memory reservation (soft limit)
uc run --memory-reservation 512m myapp:latest

Health checks

Override the image’s health check:
uc run --health-cmd "curl -f http://localhost:8000/health" \
       --health-interval 10s \
       --health-retries 3 \
       myapp:latest
Health checks monitor container status and remove unhealthy containers from Caddy load balancing.

Pull policy

Control when images are pulled from the registry:
# Always pull latest version
uc run --pull always nginx:latest

# Never pull, use local image only
uc run --pull never myapp:local

# Pull only if missing (default)
uc run --pull missing nginx

Run as different user

# Run as user 'nobody'
uc run --user nobody nginx

# Run as UID 1000, GID 1000
uc run --user 1000:1000 myapp:latest

Privileged containers

# Run with extended privileges (use with caution)
uc run --privileged system-tool
Privileged containers have access to all host devices and can break out of container isolation. Use only when absolutely necessary.

Real-world examples

PostgreSQL database

uc run --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:16

Redis cache

uc run --name redis \
  -v redis-data:/data \
  redis:alpine redis-server --appendonly yes

Web application with persistent storage

uc run --name app \
  -p app.example.com:8000/https \
  -e DATABASE_URL=postgres://postgres:secret@postgres:5432/myapp \
  -e REDIS_URL=redis://redis:6379 \
  -v app-uploads:/app/uploads \
  --cpus 2 \
  --memory 1g \
  myapp:v1.0.0

Static file server

uc run --name static \
  -p static.example.com:80/https \
  -v /var/www/static:/usr/share/nginx/html:ro \
  nginx:alpine

Service discovery

Services can communicate using DNS names:
  • service-name: Resolves to all healthy container IPs
  • service-name.internal: Same as above (explicit internal DNS)
Example:
# Deploy database
uc run --name postgres -e POSTGRES_PASSWORD=secret postgres:16

# Deploy app that connects to database using DNS name
uc run --name app \
  -e DATABASE_URL=postgres://user:secret@postgres:5432/mydb \
  myapp:latest
The app service connects to postgres using the hostname postgres, which resolves to the database container’s IP.

Next steps

Docker Compose

Deploy multi-service applications using compose.yaml

Rolling Updates

Update services with zero downtime

Scaling

Scale services horizontally across machines

Health Checks

Monitor container health during deployments

Build docs developers (and LLMs) love