I have written before about the recently released Portainer feature of deploying a Docker container stack from a git repo. A part of that feature that I didn’t immediately understand is that Portainer clones the whole repository before running the stack deployment command, and I want to explain why that can be very useful.

The TL;DR

The two main scenarios for this that came to my mind are:

  1. Use it to also deliver and update something like a configuration file
  2. Use it to deliver sources and build an image on the fly

The details: Use it to also deliver and update something like a configuration file

We are using YARP for some scenarios, which is a reverse proxy that can be configured with a JSON config file. My colleague Markus Lippert created a generic image that can do a couple of things that we need, like a custom authentication logic. But it needs configuration as you can see e.g. here in a sample provided by the YARP team because this is the mechanism to let YARP know which requests to forward and where and how to forward them. Because Portainer clones the whole repository, we can just put that configuration file next to the compose file and deployment works easily. To give you an idea, this is how the configuration file looks currently in dev:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
    "ReverseProxy": {
        "Routes": {
            "script-updater": {
                "ClusterId": "script-updater",
                "AuthorizationPolicy": "default",
                "Match": {
                    "Path": "script-updater/{**catch-all}"
                },
                "Transforms": [
                    {
                        "PathRemovePrefix": "/script-updater"
                    }
                ]
            },
            "swarm-cleanup": {
                "ClusterId": "swarm-cleanup",
                "AuthorizationPolicy": "default",
                "Match": {
                    "Path": "swarm-cleanup/{**catch-all}"
                },
                "Transforms": [
                    {
                        "PathRemovePrefix": "/swarm-cleanup"
                    }
                ]
            }
        },
        "Clusters": {
            "script-updater": {
                "Destinations": {
                    "destination1": {
                        "Address": "http://script-updater-dev/"
                    }
                }
            },
            "swarm-cleanup": {
                "Destinations": {
                    "destination1": {
                        "Address": "http://swarm-cleanup-dev/"
                    }
                }
            }
        }
    }
}

Whenever we need to make a change, we can just do that in the repo as well, use the update mechanism in Portainer and have the change delivered to the target environment. With that, we are able to have good separation between the generic image and the specific configuration while at the same time allowing for easy rollouts.

The details: Use it to deliver sources and build an image on the fly

We are not actively using that scenario at the moment, but I think it might prove handy in the future to set up environments where you want to test or debug your current dev code. To again give you an idea what this might look like: I just used the Redis and Flask web app combination which is provided as getting started sample for docker compose and put it into a publicly available Github repo. If I deploy this stack initially, it looks like this

screenshot of the deployment screen

If I then click on “Deploy the stack”, Portainer clones the whole repo and uses the instructions in the compose file to create an image and start a container with it as well as the standard Redis container. Here is the compose file:

1
2
3
4
5
6
7
8
version: "3.9"
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

Line 4 tells the Docker engine to look into the current folder for a Dockerfile and build the image. I don’t want to go into details how this particular image works because that is not the point of this blog post, but feel free to explore the repo as it is really simple. Now if I make a change to the Flask web app in the repo, I need to delete the container and image to allow a rebuild, and after that, I can just click “Pull and redeploy” to get the latest files and create an image again.

As I wrote, we are not actively using this mechanism at the moment, but I am pretty sure we will at some point in the future because like with so many features in Portainer, it just works well and is easy to use!