Since several years, I’m hosting a lot of things: blog, wiki, emails etc… I’ve played with Vserver, OpenVZ, KVM and finaly LXC. For years, I’ve learned how to use all of them but the most known solution during the last years is Docker.

I even can remeber the first Meetup I’ve attended in Paris talking about Docker 5 years ago. Now containers are eveverywhere. I recently changed the server I’m running for my own usage and this was the good timing to switch from LXC to Docker.

I’ve wrote several blog posts regarding Kubernetes and my first thoughts were about using Kubernetes. But it looks like really overkill on a single machine. I then tought about using Docker Compose and I think this is the most appropriate solution for this use case.

Install and configure Docker Compose

I’m running on a Debian server and have installed Docker from the official repository.

Then to make it simple, I’m using systemd to automatically run my containers when the server boots up /etc/systemd/system/docker-compose@.service :

Description=%i service with docker compose

# The directory where your compose config files are stored

# Remove old containers, images and volumes
ExecStartPre=/usr/bin/docker-compose down -v
ExecStartPre=/usr/bin/docker-compose rm -fv
ExecStartPre=-/bin/bash -c 'docker volume ls -qf "name=%i_" | xargs docker volume rm'
ExecStartPre=-/bin/bash -c 'docker network ls -qf "name=%i_" | xargs docker network rm'
ExecStartPre=-/bin/bash -c 'docker ps -aqf "name=%i_*" | xargs docker rm'

# Compose up
ExecStart=/usr/bin/docker-compose up

# Compose down, remove containers and volumes
ExecStop=/usr/bin/docker-compose down -v


You can add a Docker compose configuration file (docker-compose.yml) and start it:

systemctl enable docker-compose@myservice
systemctl start docker-compose@myservice

This makes container configuration very simple and easily manageable.

To finish, let’s create a dedicated network for containers (here called frontend):

docker network create frontend

Using Traefik as reverse proxy

To make containers accessible from the outside, with Let’s Encrypt TLS and connected to Docker. For this I’ve setup Traefik with Docker compose.

I’m using this Docker compose configuration file /etc/docker/compose/traefik/docker-compose.yml:

version: '3'
    image: traefik:1.7.6-alpine
    restart: always
      - "80:80"
      - "443:443"
      # WebUI admin port
      - "8082:8080"
    # Need this network to make Traefik able to forward on all containers running on this one too
      - frontend
      traefik.enable: true
      traefik.domain: ''
      traefik.tags: web,lb,traefik
      traefik.frontend.rule: 'Host:traefik.docker.local' 'frontend'
      - ./conf/traefik:/etc/traefik
      - /var/run/docker.sock:/var/run/docker.sock
      TZ: "Europe/Paris"
      # Cloudflare API credentials
      CF_API_EMAIL: my@email
      CF_API_KEY: mykey

    external: true

And the Traefik configuration /etc/docker/compose/traefik/conf/traefik/traefik.toml:

# Set default entrypoints
defaultEntryPoints = ["http", "https"]

checkNewVersion = true
MaxIdleConnsPerHost = 500
logLevel = "INFO"
sendAnonymousUsage = true

# Launch API

# Connect to Docker host to get the running docker instances
  domain = "docker.local"
  watch = true
  exposedByDefault = false

# Additional static configuration file
  filename = "/etc/traefik/rules.toml"
  watch = true

# TLS Let's Encrypt configuration with Cloudflare support
email = "my@email"
storage = "/etc/traefik/acme/acme.json"
entryPoint = "https"
OnHostRule = true
acmeLogging = true
  provider = "cloudflare"

# Request wildcard domain TLS
  main = "*"

# Run on those ports
  address = ":80"
  compress = true
  address = ":443"
  compress = true

I’m now able to test it with:

cd /etc/docker/compose/traefik/
docker-compose up

Port 80 and 443 should be open of course and they should now answer. The admin WebUI port is not open from the outside so you need a VPN or an SSH tunnel to access it for example.

Once your configuration works as expected, you enable this docker compose as a service:

systemctl enable docker-compose@traefik
systemctl start docker-compose@traefik

Wordpress configuration

For Wordpress, the documentation recommended to mount only some folders (like plugins and uploads). However, I do not especially recommand it because you’ll need to restart the container with a newer version anytime you want to upgrade it. That’s why I’m mounting all the Wordpress folder. This is not the “Container way” theory with immutable images, but from a pragmatic point of view, I prefer having a Wordpress always up to date (with dedicated plugin managing the upgrade of all components) instead of having a process managing the upgrade of my containers like Watchtower. That’s my choice and point of view, I know that other persons would choose the other solution, so select the most appropriate one.

Here is my config:

version: '3'
  # Required MariaDB instance with mounted volumes
    image: mariadb:10.3.11
    restart: always
      - ./conf/mariadb:/etc/mysql/mariadb.conf.d
      - /mnt/container/wordpress/mariadb:/var/lib/mysql
      - frontend
      MYSQL_ROOT_PASSWORD: password
  # Wordpress:expose it using labels with defined rule name
    image: wordpress:5.0.0-php7.1
    restart: always
      traefik.enable: true
      traefik.tags: web
      traefik.frontend.rule: ''
      traefik.frontend.entryPoints: https
    # Need this network to allow traefik redirect to it
      - frontend
      - "8080:80"
      - ./conf/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
      - /mnt/container/wordpress/wordpress:/var/www/html
      WORDPRESS_DB_NAME: wordpress
      - mysql
      - mysql
    external: true

Note: if you’re using it this way like me, the very first time, you’ll need to copy a wordpress content from the container to your volume. Otherwise, your Wordpress volume will be empty.

Same as Traefik, I’m now able to test it with:

cd /etc/docker/compose/wordpress/
docker-compose up

It should be now accessible from You can double check on the Traefik WebUI if your service is correctly exposed to the outside.

Once your configuration works as expected, you enable this docker compose as a service:

systemctl enable docker-compose@wordpress
systemctl start docker-compose@wordpress

I’ve finished with it. It’s a simple and smart way to manage containers on a single server, it’s repetable. Do not forget to backup your data, configuration files and you’re good to go.

Hope this helped