Contents

on Docker Compose Configs

Trying out configs spec in Docker Compose v2

As a part of the working on Komponist I was looking into the updated Docker Compose Specs and it supports some nice features like include specification which essentially makes your Compose files split up into individual service files and an entry-point YAML file which includes the these services as if they were header files, making container stacks more modular and manageable.

One more interesting spec I am currently looking into including in my project is the configs specification, which adds any sort of configuration files needed for the container to run - integrated into the compose YAML. In a nutshell, configurations can be now part of the the container’s compose YAML file and there can be less files to mount into the container. In many cases, a container’s Compose YAML file can be the complete rule that describes how it should be configured and run.

Example

As a simple example I wanted to setup a traefik reverse-proxy with a Basic Authentication logic for an example whoami container. Nothing complicated, nothing fancy.

We will use the include spec along with configs in specific Compose files.

Below is the Compose YAML file for the whoami service configured to have basic authentication when trying to reach it via command-line tools like curl or by hitting the url whoami.localhost in the browser. The authentication user and password will exist in the /etc/traefik/adminuser file.

# docker-compose.whoami.yml
# Description: Whoami container as an example

services:
  whoami:
    image: traefik/whoami:latest # don't use latest for prod!
    container_name: whoami-test
    security_opt:
      - "no-new-privileges=true"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
      - "traefik.http.routers.entrypoints=web"
      - "traefik.http.routers.middlewares=whoamiBasicAuth"
      - "traefik.http.middlewares.whoamiBasicAuth.basicauth.usersfile=/etc/traefik/adminuser"

The traefik reverse-proxy container is configured the standard way to listen on the localhost ports for HTTP (80, 8080). The configs section describes the admin user and the BCrypt encrypted password for the plain-text value testPaSS.

# docker-compose.traefik.yml
# Description: Reverse-Proxy to listen on localhost HTTP ports
services:
  traefik:
    image: traefik:latest # don't use latest for prod!
    container_name: reverse-proxy-traefik
    ports:
      - "127.0.0.1:80:80"
      - "127.0.0.1:8080:8080"
    labels:
      - "traefik.enable=true"
    security_opt:
      - "no-new-privileges=true"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command:
      - "--api-insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.web.address=:80"
    # Configs to be made available to container on runtime
    configs:
      - source: traefik_basicauth_adminuser
        target: /etc/traefik/adminuser

configs:
  traefik_basicauth_adminuser:
    content: |
      admin:$2y$08$1iQ3sGBP1ef/hAKTk2vjAOK8b66cULtTZ54lDR/4We6fpVK3SQGEq
encrypting creds in terminal
htpasswd -nb -B -C 8 admin testPaSS

The main entrypoint compose YAML file will just need to include the two above services in it using include as follows:

# compose.yml
# Description: main entrypoint compose file
name: testproject
include:
  - ./docker-compose.traefik.yml
  - ./docker-compose.whoami.yml

Configuration check

In order to be sure that there isn’t any misconfiguration perform the following command in the directory where the compose.yml file exists:

docker compose config

This command should provide a valid YAML structure in your stdout.

If there is any error there will output available with specific lines mentioning where the potential error might be.

Testing

bringing the services up:

docker compose up -d

the whoami service is now available behind the reverse-proxy under the whoami.localhost domain.

If things are correctly configured, we can perform an HTTP GET and we get a 401 Unauthorized

> curl http://whoami.localhost/
401 Unauthorized

> curl -u admin:testPaSS http://whoami.localhost/
Hostname: f1771c6c9f56
IP: 127.0.0.1
IP: ::1
IP: 172.18.0.2
RemoteAddr: 172.18.0.3:55496
GET / HTTP/1.1
Host: whoami.localhost
User-Agent: curl/8.11.1
Accept: */*
Accept-Encoding: gzip
Authorization: Basic YWRtaW46dGVzdFBhU1M=
X-Forwarded-For: 172.18.0.1
X-Forwarded-Host: whoami.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: db3ea9878cc6
X-Real-Ip: 172.18.0.1

When content in file won’t work

One needs to be careful when it comes to $ in Compose files. $ is used an interpolation logic in compose files, which could mean that any value after a $ symbol may be interpreted as if it were an environment variable that Compose will try to resolve / interpolate.

In some cases, the docker compose config command will throw a warning stating the value after $ cannot be interpolated and an empty value will be used instead.

This will also be evident with the stdout output of the docker compose config command, where anything post the $ may be left empty. In such cases, one can rely on the file parameters instead of content in the Compose file.

Alternate way for the user creds in Example

# docker-compose.traefik.yml
services:
  traefik:
    image: traefik:latest
    container_name: reverse-proxy-traefik
    ports:
      - "127.0.0.1:80:80"
      - "127.0.0.1:8080:8080"
    labels:
      - "traefik.enable=true"
    security_opt:
      - "no-new-privileges=true"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command:
      - "--api-insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.web.address=:80"
    # Configs to be made available to container on runtime
    configs:
      - source: traefik_basicauth_adminuser
        target: /etc/traefik/adminuser

configs:
  traefik_basicauth_adminuser:
    file: ./adminuser # adminuser file contains the bcrypt creds in them

This will require an external file in the directory called adminuser with the encrypted credentials in them and obtain the same results. More than one way to skin a cat!

Inference

configs spec for Compose is really great! you don’t have to worry about weird volume file-mounts, but you can try to make you service’s compose file hold almost all of the things required to run it!.

Pair it up with secrets spec and you can also handle credentials in a safer way!

A well-designed spec for sure!