Ever since I started venturing into Docker more and more, all of my services have been gradually migrated to run in their container form. It has vastly improved updating services and recreating them from scratch if needed.
At home, I have my homelab running a Debian 9 based Intel NUC with Docker and Docker Compose. I recently moved out of the house to my own place, and before I actually moved, I left my family with the HTPC setup they were used to using when I was around, only now slimmed it down so that it runs on a Raspberry Pi 3B+. Docker and Raspberry Pi go hand in hand these days, so without any hassle I set up Docker on the pi and migrated all the services I previously installed via apt to run in Docker.
Managing these services, both at home and remote, has proven to be a breeze thanks to the excellent work from the people over at linuxserver.io and Docker Compose but still, I felt like the Docker CLI commands were hard to remember and managing remote instances, like the Raspberry Pi, is very difficult with just the basic Docker toolkit.
Portainer
Luckily we have Portainer, which solves a lot of these problems!
Portainer is a UI interface and manager which you connect to the local Docker UNIX socket (unix:///var/run/docker.sock
) and then lets you view and interact with your containers, stacks and images.
At work, on my Mac, I mostly use its counterpart Kitematic, but it seems like Kitematic is macOS/Windows only and doesn't offer a lot of the features that Portainer does.
Running Portainer
You can easily set up Portainer using the following simple Docker Compose file:
yamlversion: '2'services:portainer:image: portainer/portainercontainer_name: portainercommand: -H unix:///var/run/docker.sockports:- 9000:9000volumes:- /var/run/docker.sock:/var/run/docker.sock- /opt/appdata/portainer:/datarestart: always
The tool offers some stuff of its own like user management and templates for Compose stacks, but I am not really interested in that. The most interesting parts are the viewing/tailing of containerlogs, control of containers, quick overview of unused volumes/images/networks and shells that can be spawned.
So I got Portainer running on the NUC and the Pi and the one on the NUC forwarded to an external domain so I can access it at work or remotely. But... I do not want to manage another domain name for the one on the Pi and set it up again...
Exposing Docker sockets via portainer-agent
Docker by default offers a way to expose and connect to the socket over TCP but setting it up is a true monks work as we would say over here. There are certificates to be created, signed, protected, verified and systemd services to adjust. Nobody wants to do that!
Again, Portainer is saving our asses. I looked through their docs on how to manage multiple Docker hosts from 1 portainer instance and they also referenced forwarding the socket, but instead they prefer to use the Portainer agent image to link hosts together.
The Portainer UI will tell you to copy/paste curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent
into the host you want to forward, but this only works for Docker Swarm nodes and did not work in my simple case.
Instead I had to do something way simpler: add the portainer/agent service to your Docker Compose stack on the host you want to manage, reup the stack with docker-compose up -d
and then in the manager host, just connect to the WAN IP which has port 9001 forwarded in Portainer.
yamlversion: '2'services:portainer:image: portainer/portainercontainer_name: portainercommand: -H unix:///var/run/docker.sockports:- 9000:9000volumes:- /var/run/docker.sock:/var/run/docker.sock- /opt/appdata/portainer:/datarestart: alwaysportainer_agent:image: portainer/agentcontainer_name: agentports:- 9001:9001volumes:- /var/run/docker.sock:/var/run/docker.sock- /var/lib/docker/volumes:/var/lib/docker/volumesrestart: always
Now I can easily and safely manage and update both my home and remote Docker hosts wherever I am (note that I have substituted some values in the screenshots).