1. The problem I’m having:
I’m trying to set up a Traefik Docker container to act as a proxy for several other Docker containers running Caddy, MySQL, PHP so that each of the containers behind Traefik can host a website / webapplication. Here is a quick drawing:
Without Traefik, everything works fine, I assume it’s because both Traefik and Caddy try to issue an certificate for my domain vanill.at as seen in the
docker logs -f 1876714962c4
below.
When running the Traefik container, I can only access Traefik’s dashboard at
monitor.vanill.at but not the website located inside
/srv/lcmp/www/public
which is a Shopware 6 installation but could be any static index.html file.
At this point I have no clue what to try or where to even begin to resolve this issue. since I’m fairly new to all this.
The folder structure looks like this
- /srv/
- /srv/traefik.toml
- /srv/traefik_dynamic.toml
- /srv/docker-compose.yml
- /srv/letsencrypt
++ /srv/letsencrypt/acme.json (I did a chmod 600 to it)
++ /srv/lcmp
++ /srv/lcmp/docker-compose.yml
++ /srv/lcmp/caddy_docker
+++ /srv/lcmp/caddy_docker/Caddyfile
+++ /srv/lcmp/caddy_docker/Dockerfile
++ /srv/lcmp/php_docker
+++ /srv/lcmp/php_docker/Dockerfile
++ /srv/lcmp/www
+++ /srv/lcmp/www/public
2. Error messages and/or full log output:
curl -vL
* Trying 185.164.4.42:80...
* Connected to vanill.at (185.164.4.42) port 80 (#0)
> GET / HTTP/1.1
> Host: vanill.at
> User-Agent: curl/7.76.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://vanill.at/
< Server: Caddy
< Date: Thu, 21 Mar 2024 19:24:12 GMT
< Content-Length: 0
<
* Closing connection 0
* Clear auth, redirects to port from 80 to 443Issue another request to this URL: 'https://vanill.at/'
* Trying 185.164.4.42:443...
* Connected to vanill.at (185.164.4.42) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS header, Unknown (21):
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: self-signed certificate
* Closing connection 1
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
Error in Browser
NET::ERR_CERT_AUTHORITY_INVALID
Output of docker logs -f 1876714962c4
which is the Traefik Docker container
time="2024-03-21T19:20:58Z" level=error msg="Unable to obtain ACME certificate for domains \"vanill.at\": unable to generate a certificate for the domains [vanill.at]: acme: error: 429 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-order :: urn:ietf:params:acme:error:rateLimited :: Error creating new order :: too many certificates (5) already issued for this exact set of domains in the last 168 hours: vanill.at, retry after 2024-03-22T21:13:49Z: see https://letsencrypt.org/docs/duplicate-certificate-limit/" rule="Host(`vanill.at`)" providerName=lets-encrypt.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory" routerName=caddy@docker
3. Caddy version:
docker exec -it lcmp-caddy-1 caddy version
v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=
4. How I installed and ran Caddy:
see docker-compose.yml
and Dockerfile
a. System environment:
A virtual server running AlmaLinux 9, Dockerfile Version 3.9
vanill.at is connected to the vServer using DNS A recoreds which works fine
b. Command:
To start the Docker containers running Caddy, PHP, MySQL
docker compose up -d
→ output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8855d15ac968 lcmp-caddy "caddy run --config …" 34 minutes ago Up 34 minutes 80/tcp, 443/tcp, 2019/tcp, 443/udp lcmp-caddy-1
578520a4c5d3 phpmyadmin/phpmyadmin:latest "/docker-entrypoint.…" 34 minutes ago Up 34 minutes 0.0.0.0:8080->80/tcp, :::8080->80/tcp lcmp-phpmyadmin-1
0a81677b87f0 lcmp-php "docker-php-entrypoi…" 34 minutes ago Up 34 minutes 9000/tcp lcmp-php-1
c1c35caf9124 mysql:8.0 "docker-entrypoint.s…" 34 minutes ago Up 34 minutes 3306/tcp, 33060/tcp lcmp-mysql-1
1876714962c4 traefik:v2.11 "/entrypoint.sh --ap…" 4 hours ago Up 4 hours 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp srv-traefik-1
To create the network web that should be exposed to the internet
docker network create web
→ successful
docker network inspect web
[
{
"Name": "web",
"Id": "29d19a955745e51fd0e84921fe6c09b2b895e06761b91b3b72354f39a68d0dca",
"Created": "2024-03-21T13:34:58.556968868+01:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.224.0/20",
"Gateway": "192.168.224.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"0a81677b87f02e51c46970b5a7a12824af5610906973b7761cce89f4663ddac9": {
"Name": "lcmp-php-1",
"EndpointID": "81485d2d3cafe191c7bb6bbfd2c5d1b05b2095a23cceba604d4504036da952aa",
"MacAddress": "02:42:c0:a8:e0:03",
"IPv4Address": "192.168.224.3/20",
"IPv6Address": ""
},
"1876714962c426e45039dec88719792d58f2d59262664e623c2ce97f6b32508d": {
"Name": "srv-traefik-1",
"EndpointID": "d7eee022bf04f5fb3d0ce8cddf56e82da8c97fc4a4af6ca2e9f05449f45d2825",
"MacAddress": "02:42:c0:a8:e0:02",
"IPv4Address": "192.168.224.2/20",
"IPv6Address": ""
},
"8855d15ac968632c329eec7315e157015a17267d21d18ed72370d650a4fca3e7": {
"Name": "lcmp-caddy-1",
"EndpointID": "f67a48ccc02a7310c64b5afbe962d5c42a07e64badf08956b73ddb0f78cfc583",
"MacAddress": "02:42:c0:a8:e0:04",
"IPv4Address": "192.168.224.4/20",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
c. Service/unit/compose file:
docker-compose.yml
for running the Traefik Docker container
version: '3.9'
services:
traefik:
image: traefik:v2.11
ports:
- 80:80
- 443:443
networks:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- letsencrypt:/letsencrypt
- $PWD/traefik.toml:/traefik.toml
- $PWD/traefik_dynamic.toml:/traefik_dynamic.toml
- /var/log:/var/log
command:
- --api.dashboard=true
- --log.level=INFO
- --log.filepath=/var/log/traefik.log
- --accesslog=true
- --accesslog.filepath=/var/log/traefik-access.log
- --providers.docker.network=web
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entryPoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
# remove next line when using Traefik v2
#- --entrypoints.websecure.asDefault=true
- --entrypoints.websecure.http.tls.certresolver=myresolver
- --certificatesresolvers.myresolver.acme.email=mail@vanill.at
- --certificatesresolvers.myresolver.acme.tlschallenge=true
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
labels:
- traefik.enable=true
#- traefik.http.routers.mydashboard.rule=Host(`monitor.vanill.at`)
#- traefik.http.routers.mydashboard.service=api@internal
#- traefik.http.routers.mydashboard.middlewares=myauth
#- traefik.http.middlewares.myauth.basicauth.users=<redacted>:<redacted>
networks:
web:
external: true
volumes:
letsencrypt:
name: letsencrypt
The traefik.toml
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http.redirections.entryPoint]
to = "websecure"
scheme = "https"
[entryPoints.websecure]
address = ":443"
[api]
dashboard = true`Preformatted text`
[certificatesResolvers.lets-encrypt.acme]
email = "mail@vanill.at"
storage = "acme.json"
[certificatesResolvers.lets-encrypt.acme.tlsChallenge]
[providers.docker]
watch = true
network = "web"
[providers.file]
filename = "traefik_dynamic.toml"
The traefik_dynamic.toml
[http.middlewares.simpleAuth.basicAuth]
users = [
"<redacted>:<redacted>"
]
[http.routers.api]
rule = "Host(`monitor.vanill.at`)"
entrypoints = ["websecure"]
middlewares = ["simpleAuth"]
service = "api@internal"
[http.routers.api.tls]
certResolver = "lets-encrypt"
The Dockerfile
for PHP
FROM php:8.2-fpm-alpine
# Downloading install-php-extensions script and making it executable
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
# Making install-php-extensions script executable
RUN chmod +x /usr/local/bin/install-php-extensions
# Installing PHP extensions using install-php-extensions script
RUN install-php-extensions \
mysqli \
pdo \
pdo_mysql \
gd \
zip \
intl \
xml \
curl \
dom \
fileinfo \
iconv \
json \
libxml \
mbstring \
openssl \
pcre \
phar \
simplexml \
zlib
# Setting PHP directives
RUN echo 'memory_limit = 512M' > /usr/local/etc/php/conf.d/memory-limit.ini
RUN echo 'max_execution_time = 300' > /usr/local/etc/php/conf.d/max-execution-time.ini
RUN echo 'max_input_vars = 10000' > /usr/local/etc/php/conf.d/max-input-vars.ini
RUN echo 'max_input_time = 300' > /usr/local/etc/php/conf.d/max-input-time.ini
RUN echo 'opcache.revalidate_freq=0' > /usr/local/etc/php/conf.d/opcache-revalidate-freq.ini
RUN echo 'opcache.validate_timestamps=0' > /usr/local/etc/php/conf.d/opcache-validate-timestamps.ini
RUN echo 'opcache.max_accelerated_files=7963' > /usr/local/etc/php/conf.d/opcache-max-accelerated-files.ini
RUN echo 'opcache.memory_consumption=256' > /usr/local/etc/php/conf.d/opcache-memory-consumption.ini
RUN echo 'opcache.interned_strings_buffer=16' > /usr/local/etc/php/conf.d/opcache-interned-strings-buffer.ini
RUN echo 'opcache.fast_shutdown=1' > /usr/local/etc/php/conf.d/opcache-fast-shutdown.ini
# Cleaning up
RUN rm -rf /var/cache/apk/*
Dockerfile
for Caddy
# Use the official Caddy Docker image
FROM caddy:latest
# Update package index and upgrade installed packages
RUN apk update && apk upgrade
# Copy Caddyfile to configure Caddy server
COPY Caddyfile /etc/caddy/Caddyfile
docker-compose.yml
for the Docker container that should be behind Traefik
networks:
web:
external: true
internal:
external: false
services:
# PHP Service
php:
build: './php_docker/'
volumes:
- ./www/:/var/www/html/
labels:
- traefik.enable=true
#- traefik.http.routers.php.rule=Host(`vanill.at`)
#- traefik.http.routers.php.tls=true
#- traefik.http.routers.php.tls.certresolver=lets-encrypt
#- traefik.port=9000
networks:
- internal
- web
# Caddy Service
caddy:
build: './caddy_docker/'
depends_on:
- php
restart: unless-stopped
#ports:
#- "80:80"
#- "443:443"
#- "443:443/udp"
volumes:
- ./www/:/var/www/html/
- ./caddy_docker/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
labels:
- traefik.enable=true
- traefik.tcp.routers.caddy.entrypoints.websecure
- traefik.tcp.routers.caddy.rule=HostSNI(`*`)
- traefik.tcp.services.caddy.loadbalancer.server.port=80
- traefik.http.routers.caddy.rule=Host(`vanill.at`)
- traefik.http.routers.caddy.tls=true
- traefik.http.routers.caddy.tls.certresolver=lets-encrypt
- traefik.port=443
#- traefik.port=80
- traefik.port=80
networks:
- internal
- web
# MySQL Service
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: <redacted>
volumes:
- mysqldata:/var/lib/mysql
networks:
- internal
labels:
- traefik.enable=false
# phpMyAdmin Service
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
ports:
- 8080:80
environment:
PMA_HOST: mysql
networks:
- internal
depends_on:
- mysql
# Volumes
volumes:
mysqldata:
caddy_data:
caddy_config:
d. My complete Caddy config:
encode gzip zstd
root * /var/www/html/public
php_fastcgi php:9000
file_server
header {
-server
-Link
-X-Powered-By
# disable FLoC tracking
Permissions-Policy interest-cohort=()
# enable HSTS
Strict-Transport-Security max-age=31536000;
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# clickjacking protection
X-Frame-Options DENY
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
}
}
www.vanill.at {
redir https://vanill.at{uri}
}
5. Links to relevant resources:
Here is the guide that I used to set it up
I followed this exact tutorial and got the WordPress site to run but I do not want to run this exact configuration.
Here is an image of my Traefik Dashboard at monitor.vanill.at
I highly appreciate any help or hints!