The below is a docker-compose file, in combination with a traefik configuration. I have been building it over the last months. I put it on the net, because I would have wished for something like this when I started a few months ago 🙂 .
For me it was especially difficult to understand how to “wire” the containers via Traefik to the outside and via Docker-Compose to the inside. I hope to give you a template with the file set to help you understand what you are looking for in the help files of Docker and Traefik.
Goal
- Provision of modular add-on “apps” for a small company (project management, time recording, etc.)
- Low administration effort
- Provision of professional, Internet-accessible, company infrastructure
- Allow fast testing of applications – I didn’t know what I needed, wanted to test it
- Encrypt all communication with https
Solution
- Description of servers and their networking as docker-compose-file. The simulated network consists of 9 virtual subnets and 12 virtually interconnected servers – without having to pull a wire
- One virtual server per app (example: op.my-company.de is answered by Docker-Container from OpenProject, kb.my-company.de is answered by Docker-Container from DokuWiki, etc.)
.
Targets / Non-targets of this post
I would like to give a set of files that “works” to people who have similar goals in Docker/Traefik etc. . Whoever wants to get acquainted should be able to see where you can/must go.
I can give small help with the below construction. But I can’t keep anyone from reading the documentation about Docker, Traefik and the other containers.
If you would like to commission my engineering office to set up a comparable infrastructure for your company, I would be happy to help you. The basic structure can be set up for other companies within 1-2 working days. I can recommend this in so far as it drastically shortens your training period. The conceptual part of the training, in which you have to get used to the functionality of the whole system, is omitted.
Implemented approaches
- Simple ticket system, preferably with project management and time recording functions; here I played with OpenProject, but did not use its time recording afterwards. Instead, I used Kimai. A knowledge base is implemented wiki-based; here I tested Media-Wiki, found it too cumbersome and used DokuWiki.
- An internal Team-Messaging-System; here I found Matrix/Synapse very interesting, which is used e.g. by the German Federal Armed Forces or the French State. For team messaging, I nowadays consider the Office365 solutions to be more suitable. So de facto the results of the matrix tests are only used for a protected chat environment in a private context.
Reasons among others: Matrix is really technically great in many technical things. But the current state of the art of the GUI-technology in such systems is, in my opinion, that you can “collapse” answers to a post or that answers are displayed collapsed from the beginning. Matrix does not offer this.
This causes that Matrix becomes quickly confusing. In my opinion it works only in a small scale. It is e.g. not possible to exchange messages on different topics in one channel. If someone comes back after a 3 day “matrix break” he finds an unsorted mess of text messages per channel, which are not sorted by threads. Matrix does not have message threads, although it has been desired for years. As technically beautiful as I find Matrix, they can’t be our messenger in the long run if it stays that way. Mattermost has it in the roadmap at the end of the year – if Matrix doesn’t have it then, we might jump over it. Zulip I will also check it out.
- I have also tested – as an alternative to Matrix as the Team-Messaging-System – Mattermost. I liked it very much. It was surprisingly similar to Slack. But it was not so good that I would have migrated the participants of the running Matrix installation to a new system. Also Mattermost offers no function to separate threads in channels.
- What Minecraft as game has to do with the row of company containers? I am a self-dependend father of several boys. I promote Minecraft as a computer game to the boys, because they become wonderfully creative. At the same time I want to prevent the boys from playing together with other adults. Besides, if a child’s house suddenly breaks in Minecraft, I want to know that it was one of our players so I can get it under control. Building Minecraft was easy. So the guys have their own “server”, play in their own world.
Implemented approaches
- Simple ticket system, preferably with project management and time recording functions; here I played with OpenProject, but did not use its time recording afterwards. Instead, I used Kimai. A knowledge base is implemented wiki-based; here I tested Media-Wiki, found it too cumbersome and used DokuWiki.
- An internal Team-Messaging-System; here I found Matrix/Synapse very interesting, which is used e.g. by the German Federal Armed Forces or the French State. For team messaging, I nowadays consider the Office365 solutions to be more suitable. So de facto the results of the matrix tests are only used for a protected chat environment in a private context.
Reasons among others: Matrix is really technically great in many technical things. But the current state of the art of the GUI-technology in such systems is, in my opinion, that you can “collapse” answers to a post or that answers are displayed collapsed from the beginning. Matrix does not offer this.
This causes that Matrix becomes quickly confusing. In my opinion it works only in a small scale. It is e.g. not possible to exchange messages on different topics in one channel. If someone comes back after a 3 day “matrix break” he finds an unsorted mess of text messages per channel, which are not sorted by threads. Matrix does not have message threads, although it has been desired for years. As technically beautiful as I find Matrix, they can’t be our messenger in the long run if it stays that way. Mattermost has it in the roadmap at the end of the year – if Matrix doesn’t have it then, we might jump over it. Zulip>/strong>I will also check it out.
- I have also tested – as an alternative to Matrix as the Team-Messaging-System – Mattermost. I liked it very much. It was surprisingly similar to Slack. But it was not so good that I would have migrated the participants of the running Matrix installation to a new system. Also Mattermost offers no function to separate threads in channels.
- What Minecraft as game has to do with the row of containers? I am an independent father of several boys. I promote Minecraft as a computer game to the boys, because they become wonderfully creative. At the same time I want to prevent the boys from playing together with other adults. Besides, if a child’s house suddenly breaks in Minecraft, I want to know that it was one of our players so I can get it under control. Building Minecraft was easy. So the guys have their own “server”, play in their own world.
–> Adding Minecraft into it was easy. So the guys have their own “server”, play in their own world.
Dockerfile (/var/docker_data/composed/docker-compose.yml)
version: "3.7"
networks:
private_network:
ipam:
driver: default
config:
- subnet: 192.168.200.0/24
kimai_network:
ipam:
driver: default
config:
- subnet: 192.168.201.0/24
mattermost_network:
ipam:
driver: default
config:
- subnet: 192.168.202.0/24
op_network:
ipam:
driver: default
config:
- subnet: 192.168.203.0/24
kb_network:
ipam:
driver: default
config:
- subnet: 192.168.204.0/24
gogs_network:
ipam:
driver: default
config:
- subnet: 192.168.205.0/24
synapse_network:
ipam:
driver: default
config:
- subnet: 192.168.206.0/24
gitlab_network:
ipam:
driver: default
config:
- subnet: 192.168.207.0/24
public_network:
driver: bridge
services:
traefik:
image: traefik:latest
container_name: traefik
ports:
- "80:80"
- "8080:8080"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/docker_data/traefik:/etc/traefik
# block "labels" is not really needed and without function here
labels:
- "traefik.http.routers.traefik.rule=Host(`traefik.my-company.de`)"
- "traefik.http.routers.traefik.entrypoints=https,http"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.tls.certresolver=traefikresolver"
# let Traefik snif in all networks; target: recognize ports which are opened
# and must be forwarded from outside to the containers
networks:
- private_network
- kimai_network
- mattermost_network
- op_network
- kb_network
- gogs_network
- synapse_network
- public_network
myprivatechat:
image: matrixdotorg/synapse
restart: always
volumes:
- /var/docker_data/myprivatechat:/data
# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
# ports:
# - "80:80"
# - "443:443"
labels:
- "traefik.http.routers.myprivatechat.rule=Host(`wir.myprivatechat.de`)"
- "traefik.http.routers.myprivatechat.entrypoints=https,http"
- "traefik.http.routers.myprivatechat.tls=true"
- "traefik.http.routers.myprivatechat.tls.certresolver=myprivatechatresolver"
# - "traefik.http.routers.matrix.port=8008"
container_name: myprivatechat
depends_on:
- db
networks:
- synapse_network
op:
image: openproject/community:10
restart: always
# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
# ports:
# - "80:80"
# - "443:443"
labels:
- "traefik.http.routers.op.rule=Host(`op.my-company.de`)"
- "traefik.http.routers.op.entrypoints=https,http"
- "traefik.http.routers.op.tls=true"
- "traefik.http.routers.op.tls.certresolver=opresolver"
# - "traefik.http.routers.matrix.port=8008"
environment:
- SECRET_KEY_BASE=your_SECRET_KEY_BASE
volumes:
- /var/docker_data/op/pgdata:/var/openproject/pgdata
- /var/docker_data/op/assets:/var/openproject/assets
container_name: op
networks:
- op_network
kb:
image: 'docker.io/bitnami/dokuwiki:0.20180422.202005011246-debian-10-r67'
restart: always
# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
# ports:
# - "80:80"
# - "443:443"
labels:
- "traefik.http.routers.kb.rule=Host(`kb.my-company.de`)"
- "traefik.http.routers.kb.entrypoints=https,http"
- "traefik.http.routers.kb.tls=true"
- "traefik.http.routers.kb.tls.certresolver=kbresolver"
# - "traefik.http.routers.matrix.port=8008"
volumes:
- /var/docker_data/kb:/bitnami/dokuwiki
container_name: kb
networks:
- kb_network
gitlab:
image: gitlab/gitlab-ce:latest
restart: "no"
hostname: gitlab.my-company.de
# gitlab did not work on strato v-servers
# the "container" is currently primarily an
# example for "commenting out" a container
# --> set start command for container to
# the directly exiting "true"
command: "true"
entrypoint: "true"
environment:
- GITLAB_OMNIBUS_CONFIG="external_url 'https://gitlab.my-company.de/'"
# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
# ports:
# - "2222:22"
# - "443:443"
labels:
- "traefik.http.routers.gitlab.rule=Host(`gitlab.my-company.de`)"
- "traefik.http.routers.gitlab.entrypoints=https,http"
- "traefik.http.routers.gitlab.tls=true"
- "traefik.http.routers.gitlab.tls.certresolver=gitlabresolver"
# - "traefik.http.routers.matrix.port=8008"
volumes:
- /var/docker_data/gitlab/data:/var/opt/gitlab
- /var/docker_data/gitlab/logs:/var/log/gitlab
- /var/docker_data/config:/etc/gitlab
container_name: gitlab
networks:
- gitlab_network
db:
image: postgres:alpine
environment:
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD=YOUR_MATRIX-DB_PW
# ensure the database gets created correctly
# https://github.com/matrix-org/synapse/blob/master/docs/postgres.md#set-up-database
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
# ports:
# - "127.0.0.1:5432:5432"
volumes:
# You may store the database tables in a local folder..
- /var/docker_data/myprivatechatDB:/var/lib/postgresql/data
# .. or store them on some high performance storage for better results
# - /path/to/ssd/storage:/var/lib/postgresql/data
networks:
- synapse_network
gogs:
image: gogs/gogs
container_name: gogs
volumes:
- /var/docker_data/gogs/repositories:/home/git/gogs-repositories
- /var/docker_data/gogs/data:/data
# SSH port must be routed b the ports statement
# web port is done by Traefik (see below)
ports:
# - "443:3000"
- "10022:22"
networks:
- gogs_network
depends_on:
- gogsdb
labels:
- "traefik.http.routers.gogs.rule=Host(`gogs.my-company.de`)"
- "traefik.http.routers.gogs.entrypoints=https,http"
- "traefik.http.routers.gogs.tls=true"
- "traefik.http.routers.gogs.tls.certresolver=gogsresolver"
- "traefik.http.routers.gogs.service=gogs@docker"
- "traefik.http.services.gogs.loadbalancer.server.port=3000"
gogsdb:
image: mysql:5.7.16
container_name: gogsdb
environment:
# - MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_ROOT_PASSWORD=your-gogsPW
- MYSQL_DATABASE=gogs
- MYSQL_USER=gogs
- MYSQL_PASSWORD=your-gogsPW
volumes:
- /var/docker_data/gogsdb:/var/lib/mysql
expose:
- "3306"
networks:
- gogs_network
labels:
- "traefik.enable=false"
minecraft:
image: itzg/minecraft-bedrock-server
container_name: minecraft
environment:
- EULA=TRUE
- VERSION=latest
- GAMEMODE=creative
- SERVER-NAME=your-minecraft-server-name
- MOTD="Willkommen im xxxxxxx"
- PUBLIC=false
labels:
- "traefik.enable=false"
ports:
- "19132:19132/udp"
volumes:
- /var/docker_data/minecraft:/data
kimai_sqldb:
image: mysql:5.7
environment:
- MYSQL_DATABASE=kimai
- MYSQL_USER=kimaiuser
- MYSQL_PASSWORD=your_kimaipassword
- MYSQL_ROOT_PASSWORD=your_rootPassword
volumes:
- /var/docker_data/kimaidb/:/var/lib/mysql
command: --default-storage-engine innodb
restart: unless-stopped
healthcheck:
test: mysqladmin -pyour_kimaipassword ping -h localhost
interval: 20s
start_period: 10s
timeout: 10s
retries: 3
networks:
- kimai_network
labels:
- "traefik.enable=false"
kimai:
image: bkraul/kimai2:nginx-alpine
environment:
# web server
- PHP_DATE_TIMEZONE=Europe/Berlin
# kimai2
- APP_ENV=prod
- DATABASE_URL=mysql://kimaiuser:your_kimaipassword@kimai_sqldb/kimai
- MAILER_FROM=kimai@mydomain.com
- MAILER_URL=smtp://validsmtpurl
- APP_SECRET=your_SuperSecret
# initial admin user
- APP_ADMIN_USER=master
- APP_ADMIN_EMAIL=master@mydomain.com
- APP_ADMIN_PASS=your_masterPassword
- APP_LOG=0
volumes:
- /etc/localtime:/etc/localtime:ro
- ./local.yaml:/app/config/packages/local.yaml
# ports:
# - '8001:80'
restart: unless-stopped
depends_on:
- kimai_sqldb
labels:
- "traefik.http.routers.kimai.rule=Host(`kimai.my-company.de`)"
- "traefik.http.routers.kimai.entrypoints=https,http"
- "traefik.http.routers.kimai.tls=true"
- "traefik.http.routers.kimai.tls.certresolver=kimairesolver"
networks:
- kimai_network
kimai_postfix:
image: catatnight/postfix:latest
environment:
- maildomain=kimai.local
- smtp_user=kimai:kimai
restart: unless-stopped
networks:
- kimai_network
labels:
- "traefik.enable=false"
Traefik ( /var/docker_data/traefik/traefik.toml )
Core functions of the following file:
- Monitoring the port clearances of started Docker containers via the socket of the Docker service (see below, docker.sock).
- Reroute all accesses to the virtual hosts (as defined as labels in docker-compose) to the containers.
- Receive http requests; redirect http requests to https
- For https accesses on-the-fly via Let’s-Encrypt certificates, store and manage them, handle encryption… forward unencrypted traffic to port 80 of the containers.
- Cool here: Traefik does everything fully automatic. Hardly any explanation is needed.
- It’s difficult: Traefik does everything fully automatic. Nobody explains, because it is so simple (????)…
I am desperate when setting up the system, that the setting up of Traefik was apparently sooooooo simple, that it apparently did not need any explanation. I was sitting in front of a jumble of colorful help pages for Traefik and didn’t even know what the vocabulary of my problems in Traefik was, so I couldn’t search for the solutions.
Find below a complete system consisting of Traefik configuration and a Docker-Compose-File.
logLevel = "DEBUG"
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.http]
[entryPoints.http.http.redirections]
[entryPoints.http.http.redirections.entryPoint]
to = "https"
scheme = "https"
[entryPoints.https]
address = ":443"
[api]
dashboard = true
insecure = false
[certificatesresolvers.traefikresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/traefik/acme.json"
[certificatesresolvers.traefikresolver.acme.httpChallenge]
entryPoint = "http"
#onHostRule = true
#acmeLogging =true
[acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.myprivatechatresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/myprivatechat/acme.json"
[certificatesresolvers.myprivatechatresolver.acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.mattermostnmresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/mattermostnm/acme.json"
[certificatesresolvers.mattermostnmresolver.acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.kimairesolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/kimai/acme.json"
[certificatesresolvers.kimairesolver.acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.opresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/op/acme.json"
[certificatesresolvers.opresolver.acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.kbresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/kb/acme.json"
[certificatesresolvers.kbresolver.acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.gitlabresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/gitlab/acme.json"
[certificatesresolvers.gitlabresolver.acme.httpChallenge]
entryPoint = "http"
[certificatesresolvers.gogsresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/gogs/acme.json"
[certificatesresolvers.gogsresolver.acme.httpChallenge]
entryPoint = "http"
[providers.docker]
endpoint = "unix:///var/run/docker.sock"
# exposedByDefault = false