Some time after originally publishing this recipe, I had the opportunity to meet the Portainer team, who are based out of Auckland, New Zealand. We now have an ongoing friendly working relationship. For a time, Portainer was my GitHub Sponsor , and in return, I maintained their official Kubernetes helm charts!
Portainer is a lightweight sexy UI for visualizing your docker environment. It also happens to integrate well with Docker Swarm clusters, which makes it a great fit for our stack.
Portainer attempts to take the "geekiness" out of containers, by wrapping all the jargon and complexity in a shiny UI and some simple abstractions. It's a great addition to any stack, especially if you're just starting your containerization journey!
I am all of the Sith!
In 2021, Portainer released "Dark Mode". Here's why I think this is 100% my fault :)
- Docker swarm cluster with persistent shared storage
- Traefik configured per design
- DNS entry for the hostname you intend to use (or a wildcard), pointed to your keepalived IP
Setup data locations
Create a folder to store portainer's persistent data:
Portainer Docker Swarm config
Create a docker swarm config file in docker-compose syntax (v3), something like the example below:
Fast-track with premix! 🚀
I automatically and instantly share (with my sponsors) a private "premix" git repository, which includes necessary docker-compose and env files for all published recipes. This means that sponsors can launch any recipe with just a
git pull and a
docker stack deploy 👍.
🚀 Update: Premix now includes an ansible playbook, so that sponsors can deploy an entire stack + recipes, with a single ansible command! (more here)
version: '3.2' services: portainer: image: portainer/portainer-ce command: -H tcp://tasks.agent:9001 --tlsskipverify ports: - "9000:9000" - "8000:8000" volumes: - /var/data/portainer:/data networks: - internal - traefik_public deploy: mode: replicated replicas: 1 placement: constraints: [node.role == manager] labels: # traefik - traefik.enable=true - traefik.docker.network=traefik_public # traefikv1 - traefik.frontend.rule=Host:portainer.example.com - traefik.port=9000 # uncomment if you want to protect portainer with traefik-forward-auth using traefikv1 # - traefik.frontend.auth.forward.address=http://traefik-forward-auth:4181 # - traefik.frontend.auth.forward.authResponseHeaders=X-Forwarded-User # - traefik.frontend.auth.forward.trustForwardHeader=true # traefikv2 - "traefik.http.routers.portainer.rule=Host(`portainer.example.com`)" - "traefik.http.routers.portainer.entrypoints=https" - "traefik.http.services.portainer.loadbalancer.server.port=9000" # uncomment if you want to protect portainer with traefik-forward-auth using traefikv2 # - "traefik.http.routers.portainer.middlewares=forward-auth" agent: image: portainer/agent volumes: - /var/run/docker.sock:/var/run/docker.sock - /var/lib/docker/volumes:/var/lib/docker/volumes networks: - internal deploy: mode: global placement: constraints: [node.platform.os == linux] networks: traefik_public: external: true internal: driver: overlay ipam: config: - subnet: 172.16.13.0/24
Setup unique static subnets for every stack you deploy. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot. See my list here.
Umm.. didn't you just copy these from the official Portainer docs?
Almost word-for-word! I've made a few (opinionated) improvements though:
- Expose Portainer via Traefik with valid LetsEncrypt SSL certs
- Optionally protected Portainer's web UI with OIDC auth via Traefik Forward Auth
- Use filesystem paths instead of Docker volumes for maximum "swarminess" (We want an HA swarm, and HA Docker Volumes are a PITA, so we just use our ceph shared storage)
Launch Portainer stack
Launch the Portainer stack by running
docker stack deploy portainer -c <path -to-docker-compose.yml>
Log into your new instance at https://YOUR-FQDN. You'll be prompted to set your admin user/password on first login. Start at "Home", and click on "Primary" to manage your swarm (you can manage multiple swarms via one Portainer instance using the agent):
Chef's notes 📓
Tip your waiter (sponsor) 👏
Did you receive excellent service? Want to compliment the chef? (..and support development of current and future recipes!) Sponsor me on Github / Ko-Fi / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏
Employ your chef (engage) 🤝
Is this too much of a geeky PITA? Do you just want results, stat? I do this for a living - I'm a full-time Kubernetes contractor, providing consulting and engineering expertise to businesses needing short-term, short-notice support in the cloud-native space, including AWS/Azure/GKE, Kubernetes, CI/CD and automation.
Learn more about working with me here.
Flirt with waiter (subscribe) 💌
Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated.