Wojtek @suda Siudzinski

Python/Node/Golang/Rust developer, DIY hacker, rookie designer, 3D print junkie. CEO @ Gaia Charge

Migrating from Docker Cloud to Docker Swarm with Portainer

The announcement that Docker Cloud is shutting down was sudden for everyone. It wasn't much of a surprise as it wasn't updated in a long time. But lack of transparency/reponse from Docker and the decision of giving only 60 days to migrate out of DC made a lot of people angry/anxious. Looking at alternatives there seem to be two viable ones in my eyes:

I know there are many other 3rd party tools like Rancher, Cloud 66 or Kontena but honestly I'm afraid they'll end up sharing the fate of Tutum/Docker Cloud.

Kubernetes is amazing powerhouse and deserves its own post but in here I'll focus on easier migration to Docker Swarm.

First step

Reach out to Docker and ask for extension. By default you get 60 days but they can extend it to 120 days if you ask. You might not need it but I'd recommend doing it just in case.

What will change?

Docker Cloud agent installs very old Docker engine version 1.11.2-cs5 released almost two years ago (we should've known right...) so updating to modern version will come with many nice improvements like Swarm mode, support for secrets, routing mesh, rolling updates, overlay2 storage driver and more (yeah, we missed a lot...).

Warning: Upgrading Docker will cause a downtime until the migration is complete so either provision new machines and migrate to them or if you're ok with it, perform the upgrade while apps are in "maintenance".

Before upgrading

If you're using data volumes you might need to perform some additional steps. Unfortunately volumes created automatically by Docker Cloud don't have human readable names, only a hash. Therefore it's hard to know to which service they belong. You can run this one-liner to peek into all running containers and output their ID and currently mounted volumes:

$ docker ps -q | \
    while read p; do \
        docker inspect $p | sed -n -e '/"Id"/,/"Created"/ p'; \
        docker inspect $p | sed -n -e '/"Mounts"/,/"Config"/ p'; \
        echo -e "\n\n\n"; \

You can copy this output somewhere as it will be needed later.

Upgrading Docker

First we need to remove the old Docker Cloud agent, following the docs:

$ apt-get remove dockercloud-agent

This should keep all your images, volumes etc in /home/docker directory. Docker CE installation will keep everything in /var/lib/docker so the next step is to copy (you can move but it's nice to have a backup) them there:

$ cp -R /home/docker /var/lib

Now to proceed with installation of latest docker following docs:

$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
$ sudo apt-get update
$ sudo apt-get install docker-ce

Note: you can use edge channel instead of stable but it's not recommended for production.

At this point you should have a running Docker CE with access to your past data, which can be verified with:

$ sudo docker images

Starting a Swarm

This step is optional but if you're working with more than one machine and/or you want to use Services and Stacks you need to create a Swarm. Again, following the docs:

$ docker swarm init

If you have more nodes, it is recommended to at least three of them are managers (to maintain fault tolerance). You can make two more join the swarm using the manager token from the command above:

$ docker swarm join --token MANAGER_TOKEN

On the worker nodes you can run:

$ docker swarm join --token WORKER_TOKEN

Starting Portainer

Portainer is a lightweight UI for Docker and is a great replacement for the Docker Cloud UI. You can try the demo here.

To start Portainer, you can create it as a service. First create a volume to persist the config:

$ docker volume create portainer-data

Then start the service:

$ docker service create \
    --name portainer \
    --publish 9000:9000 \
    --constraint 'node.role == manager' \
    --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
    --mount src=portainer-data,dst=/data \
    portainer/portainer \
    -H unix:///var/run/docker.sock

Now the web UI should be available on the port 9000 of any of your nodes thanks to the routing mesh.

Migrating the Stackfiles

The Stackfiles are basically Docker Compose 1.0 files with some additional options.

Removing unsupported options

The additions are not supported by regular Docker and need to be removed fromt the Stackfile:

  • autodestroy
  • autoredeploy
  • deployment_strategy
  • net
  • roles
  • sequential_deployment
  • tags
  • target_num_containers
Options not supported by docker stack deploy command

Additionally following options are not supported when deploying a stack:

  • build
  • cap_add
  • cap_drop
  • cgroup_parent
  • container_name
  • depends_on
  • devices
  • tmpfs
  • external_links
  • links
  • network_mode
  • restart
  • security_opt
  • stop_signal
  • sysctls
  • userns_mode

Upgrading Docker Compose file version

That one is easier one. The biggest difference between version 1.0 and the later ones is that services instead of being top-level objects, are now properties of services key and have a version key that specifies the version:

  image: nginx
version: '3.6'
    image: nginx

Using volumes created by Docker Cloud

To use existing volumes you need to know their ID. If you followed the step above you should have this information. Now you need to add it to the Compose file:

    external: true

    image: nginx
      - type: volume
        source: YOUR_VOLUME_ID


Once the Stackfile is migrated you can upload it to Portainer and deploy the stack!

Repeat this for all your stacks and you're done!

P.S.: Bugsnag has a nice Swarm lessons learned blogpost that can help with running Swarm in production.

comments powered by Disqus