Skip to content

Portainer

Tip

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 :)

Portainer Screenshot

Portainer Requirements

Ingredients

Already deployed:

Related:

Preparation

Setup data locations

Create a folder to store portainer's persistent data:

mkdir /var/data/portainer

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

Note

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)

Serving

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):

Portainer Home

Chef's notes 📓


  1. There are some schenanigans you can do to install LinuxServer.io templates in Portainer. Don't go crying to them for support though! 😿 

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.

Your comments? 💬