Port Docker Compose to Docker Swarm

See what we're up to: Runway is here! 🚀

Port your docker-compose project to Docker Swarm #

Planetary Quantum is based on Docker Swarm. The configuration for that is very similar to docker-compose.yml files, but it differs in some aspects:

Local Testing #

We recommend you enable Swarm on your local Docker installation:

$ docker swarm init

You can then deploy your existing docker-compose.yml files via

$ docker stack deploy up -c docker-compose.yml --prune --with-registry-auth

Docker Swarm will then print warnings for all the unsupported elements.

Once you resolved all these (check out our hints below), chances are your project will work on Planetary Quantum out of the box.

See also: Making sure your Swarm Project works on Planetary Quantum.

Global options #

version #

On Planetary Quantum, use

version: '3.8'

Unsupported service options #

build #

Is an error on Swarm. Instead, build your containers on your CI system and push them to a private registry beforehand.

You can do that with your CI system, manually, or with quantum-cli.

Then, replace the build with an

image: your-registry.example.org/your/image:some-version

You will need to set up up your private registry in Planetary Quantum, if you haven’t already.

container_name #

Ignored on Swarm. Containers are named automatically, because there may be more than one per service (if you scale up the service).

depends_on #

Ignored on Swarm. Swarm services restart when they fail by default (but see restart below), so in most setups, you can just remove depends_on.

In the few cases where restarts are too costly, or otherwise not possible, we recommend using scripts like wait-for-it.sh in the startup code of containers, so the actual workload doesn’t start until all dependencies are reachable.

expose #

Ignored on Swarm. Not needed, open ports are automatically exposed for the network(s) the container is in.

Ignored on Swarm. Instead, put containers into a network, if they need to talk to each other.

If you don’t specify any networks:, all services within a stack are in an automatically created default network. That may not be what you want, so it’s usually better to be explicit:

version: '3.8'

services:
  some-server:
    image: nginx
    networks:
      - my-internal-net

  some-client:
    image: alpine
    command: curl some-server
    networks:
      - my-internal-net

networks:
  my-internal-net:

To get services in different stacks to talk to each other, create a network first, via the Quantum Console:

  1. Choose “Networks” => “Add network”
  2. put in a name
  3. choose the “Overlay” driver
  4. and switch on “Enable manual container attachment”

You can then use that network in your stacks like so:

version: '3.8'

services:
  foo:
    image: nginx
    networks:
      - my-external-net

networks:
  my-external-net:
    external: true
    name: my-external-net

DNS names #

Services in the same network can see each other under a couple of different hostnames. For a service named my-service, in a stack called my-stack:

  • just my-service

    Do not use this name if the service is in a network that spans multiple stacks! You might have the same service name in two stacks, and then it's random which one it resolves to. On Quantum, watch out especially for services that are in the `public` network (the one Traefik uses). Even if you add your own stack-internal network, always connect to these services via a fully-qualified DNS name - see below

  • tasks.my-stack_my-service

    Preferred, especially with external networks, because it always selects the correct service.

labels #

Docker Swarm has two sets of labels - one on the containers, which are under labels, as in docker-compose. And another set on the services, which are under deploy.labels.

The distinction is only relevant if you use tools or services that read labels.

**Traefik labels on Planetary Quantum need to go into `deploy.labels`** - see also [Using the Traefik Load-balancer](/common-tasks/portainer-traefik/).
version: '3.8'

services:
  foo:
    image: nginx
    deploy:
      labels:
        org.example.my-label: my-value

ports #

Still supported on Docker Swarm, but often not needed. Use networks instead, for containers which should only be reachable to other containers.

For HTTP(S) connections to the outside world (including REST APIs, etc), use the Traefik Load-balancer.

restart #

Ignored on Swarm. The default Docker Swarm behavior is equivalent to restart: always.

restart: no (the default with docker-compose) is

version: '3.8'

services
  foo:
    image: alpine
    deploy:
      restart_policy:
        condition: none

Other options are available, including ways to specify delays and a maximum number of retries before giving up - see the Docker documentation on restart_policy.

Other Swarm-incompatible service options #

All of these are ignored on Docker Swarm:

  • cap_add and cap_drop
  • cgroup_parent
  • devices
  • network_mode
  • security_opt
  • tmpfs
  • userns_mode

Swarm-exclusive features #

Docker has a few guides for your consideration: