Docs/ Deployment/ Docker Compose

Docker Compose

Deploy a docker-compose.yml as one sh0 app: each service becomes a container on a private network, with named volumes and a single public address.

Overview

sh0 imports a Compose file as one app. Every service becomes a container started from its image, in the order given by depends_on. Services cannot be built: a service that declares build: is refused, so build its image elsewhere or deploy it as a separate app from its Git repository.

Deploying a stack

In the dashboard, open a stack, click Add App, choose Docker Compose, give the app a name, paste the file into docker-compose.yml and click Deploy Now. From the CLI, sh0 compose validate checks a file and sh0 compose deploy deploys it. Example with a web service and a Redis cache:

docker-compose.yml
services:
  web:
    image: traefik/whoami:v1.10
    ports:
      - "8080:80"
    environment:
      REDIS_URL: redis://cache:6379
    depends_on:
      - cache
    deploy:
      resources:
        limits:
          memory: 64M
          cpus: "0.25"
    healthcheck:
      test: ["CMD", "true"]
  cache:
    image: redis:7-alpine
    mem_limit: 128m
    cpus: 0.5
    volumes:
      - cache-data:/data
volumes:
  cache-data:
Terminal
$ sh0 compose validate docker-compose.yml
[ok] Compose file is valid
  Services: web, cache

$ sh0 compose deploy --app-name my-stack --wait docker-compose.yml
[*] Deploying compose file 'docker-compose.yml' as 'my-stack'...
[ok] Compose stack 'my-stack' deployed (app ID: 79389ae4-4a59-42a5-b8f6-37a018c95ddc, status: deploying)
[*] Waiting for deployment to complete...
[ok] Deployment completed successfully

--var KEY=VALUE (repeatable) replaces the value of the environment variable KEY in every service that defines it.

What validation checks

A file is refused if it has no service, if a service has no image, declares build:, has an invalid port or an unparsable command, or mounts a host path (/srv/data:/data, ./data:/data): only named volumes are allowed. Keys that sh0 does not read are not reported: the example above passes validation although its deploy and healthcheck blocks are ignored.

Compose keys

Keys read by sh0:

  • image: required, pulled from Docker Hub or any registry.
  • ports: only the container port is used. The first service, in file order, that declares a port receives the app's public address through the reverse proxy. The host port you write is not opened on the server.
  • environment: map or list form.
  • volumes: named volumes only. A volume cache-data of the app my-stack is created as sh0-my-stack-cache-data and persists across restarts.
  • depends_on: start order, list or extended form.
  • command: string or list.
  • mem_limit and cpus: the service's memory and CPU limits. These are the only resource keys sh0 reads.

Ignored keys

deploy (including deploy.resources), healthcheck, entrypoint, restart, labels and networks are accepted and ignored. Every container gets the restart policy unless-stopped, and a service without mem_limit or cpus gets the default limits. On the example stack, the 64 MB limit written under deploy.resources did not apply, while mem_limit: 128m did:

Terminal
$ docker inspect -f "{{.Name}} mem={{.HostConfig.Memory}} nanocpus={{.HostConfig.NanoCpus}} restart={{.HostConfig.RestartPolicy.Name}}" \
    sh0-17b83e1c-my-stack-web sh0-17b83e1c-my-stack-cache
/sh0-17b83e1c-my-stack-web mem=536870912 nanocpus=1000000000 restart=unless-stopped
/sh0-17b83e1c-my-stack-cache mem=134217728 nanocpus=500000000 restart=unless-stopped

Networking

The services of a stack share a private network and reach each other by service name: in the example, the cache container gets an answer from http://web:80. Only the service that holds the public address is reachable from outside.

Managing the services

The stack appears as a single app in sh0 apps and in the dashboard. Its services are listed by GET /api/v1/apps/<app-id>/services and can be restarted, stopped or started one by one with POST /api/v1/apps/<app-id>/services/<service-id>/restart (or /stop, /start).

Changing a deployed stack

Version 1.10.2 has no way to apply a new version of the Compose file to an existing stack. Deploying the file again under the same name is refused, and redeploying the app fails because it has no repository:

Terminal
$ sh0 compose deploy --app-name my-stack docker-compose.yml
[*] Deploying compose file 'docker-compose.yml' as 'my-stack'...
Error: An app with name 'my-stack' already exists (VALIDATION_ERROR)

$ sh0 deploy my-stack --wait
[*] Deploying my-stack...
  Deployment ID: 34f99c19-adda-4a30-a1dc-4e670a3ff505
  Status:        queued
[*] Waiting for deployment to complete...
error: Deployment failed.

# Build log of that deployment:
[STEP 1/6] Cloning repository
[ERROR] App has no repository URL configured

The running services are not affected by these failures. To change the stack, deploy the new file under a new name, move the traffic and the data, then delete the old app; sh0 delete removes an app and its resources.

Warning
Do not commit real secrets in a Compose file. Keep a placeholder value in environment and set the real one at deployment time with --var.