So, I'm using traefik 3 with the docker provider and I essentially have 3 routable services:
Traefik dashboard (http; port 8080)
My website (https; port 443)
phpmyadmin (http; port 80)
Here is the relevant parts of my docker-compose.yml:
services:
traefik:
# The official v3 Traefik docker image
image: traefik:v3.1
# Enables the web UI and tells Traefik to listen to docker
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "8080:8080"
- "80:80"
- "443:443"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ./certs:/etc/certs
- /.docker/local/traefik.config.yml:/etc/traefik/dynamic/traefik.yml
ttp-php:
hostname: ttp.dev
container_name: ttp-cli-php
labels:
- "traefik.enable=true"
- "traefik.http.routers.ttp.rule=Host(`ttp.dev`)"
- "traefik.http.routers.ttp.entrypoints=websecure"
- "traefik.http.routers.ttp.tls.domains[0].main=ttp.dev"
platform: linux/x86_64
build:
context: .
env_file:
- ./secrets/app_secrets.env
environment:
PHP_POST_MAX_SIZE: "500M"
PHP_UPLOAD_MAX_FILE_SIZE: "500M"
HTTPS: "on"
phpmyadmin:
image: phpmyadmin
env_file:
- ./secrets/db_secrets.env
labels:
- "traefik.enable=true"
- "traefik.http.routers.phpmyadmin.rule=Host(`phpmyadmin.dev`)"
- "traefik.http.routers.phpmyadmin.entrypoints=web"
In my /etc/hosts
file I have:
127.0.0.1 ttp.dev phpmyadmin.dev
And finally in my traefik.config.yml
:
tls:
certificates:
- certFile: /etc/certs/bundle.crt
keyFile: /etc/certs/bundle.key
stores: default
The traefik dashboard at http://localhost:8080
loads up just fine.
My website at https://ttp.dev
loads up just fine with the correct certificate.
But http://phpmyadmin.dev
redirects to https and traefik then tries to serve up a default generated certificate. Whereas it should just serve the phpmyadmin service over http on port 80. It feels like it is trying to send the requests to my ttp-php
service. Any ideas??
Thanks in Advance,
Jon
a) check the Traefik panel (:8080) for how the actual configuration for your phpmyadmin.dev
is configured and check it for any other routes on your web
entrypoint - perhaps you've configured some default redirect?
b) can you share more of the traefik.yml
?
Hey, thanks for getting back to me.
That is literally everything I have in the traefik.yml file.
In the traefik portal, everything looks perfect.
I've added an additional service; lpc.dev. Same thing, redirects to https.
Can you go into that http
service? The routers tab doesn't list used middlewares, and you seem to have 2 defined...
EDIT: NVM: those two middlewares are probably just the two that come by default with Traefik. My mistake.
I've only just realised you have a bit of a strange configuration - where you seem to provide the static configuration in the docker command part itself, and then have a dynamic setup in your traefic.yml
file. While not necessarily an error it'll likely confuse people (like myself ). I'd recommend having a static configuration traefik.yml
file and have that provided in the command, and then keep a dynamic location for anything else (named accordingly). But that shouldn't be the cause of your issue.
Ok, so I've figured out my issue:
Google Chrome redirects .dev domains to HTTPS by default as part of its initiative to secure website connections. This is because Google owns .dev domains and forces HTTPS at the registry level, so there's no way to override the redirect.
It's purely my use of .dev domains locally that is the issue!
However, I'm interested by your comment around my config.
Ideally, I'd like to keep everything in my docker-compose.yml file, but it seems that the certificates have to be configured in a file. So I've created a yml file with only the certificates in it and configured everything else via docker-compose labels. Does that make sense or would you suggest doing it differently?
1 Like
Regarding configuration, here's what I suggest based on my current setup.
docker-compose.yml
- don't use command:
at all, rather bind a static configuration file instead:
traefik:
image: "traefik:v3.1"
container_name: "traefik"
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt/:/letsencrypt/" #this is where I keep LE certificates; possibly use a docker volume so these don't get generated on each restart
- "./traefik.yml:/etc/traefik/traefik.yml" #static Traefik config
- "./services/:/services/" #dynamic configuration files
- "./certs/:/certs/" #any other certificates folder
- "/var/log/traefik/:/var/log/traefik/" #logs if you need them
labels: #if you're fine using port :8080 then the labels can be ignored (just add the 8080 port mapping!)
#note that this is just based on my config and may not apply!
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.domain.local`)"
- "traefik.http.routers.traefik.entrypoints=web"
- "traefik.http.routers.traefik.middlewares=redirect_to_https@file" #I've defined a https redirect middleware in a file for re-use
- "traefik.http.routers.traefik-https.rule=Host(`traefik.domain.local`)"
- "traefik.http.routers.traefik-https.entrypoints=websecure"
- "traefik.http.routers.traefik-https.service=api@internal"
- "traefik.http.routers.traefik-https.middlewares=local-limit@file" #I've defined an IP whitelist middleware for re-use
healthcheck:
test: "traefik healthcheck"
retries: 12
interval: 5s
Then you have your static configuration in traefik.yml
:
global:
checkNewVersion: true
sendAnonymousUsage: false
entryPoints:
web:
address: :80
websecure:
#do you want SSL as default?
asDefault: true
address: :443
#example of defaults for TLS for public domains, using a wildcard cert
http:
tls:
certResolver: le
domains:
- main: "*.domain.com"
sans:
- "domain.com"
# most reverse-proxies I've worked with don't validate internal SSL certificates, so I use a similar setting here
serversTransport:
insecureSkipVerify: true
log:
level: INFO
accessLog:
filePath: /var/log/traefik/access.log
bufferingSize: 100
api:
insecure: true
ping: {}
providers:
file:
directory: /services/
watch: true
docker:
network: traefik-proxy
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
le:
acme:
email: admin@domain.com
storage: /letsencrypt/acme.json
caserver: "https://acme-v02.api.letsencrypt.org/directory"
dnsChallenge:
provider: azuredns #remember that dnsChallenge is needed for wildcards!
le-staging:
acme:
email: admin@domain.com
storage: /letsencrypt/acme-staging.json
caserver: "https://acme-staging-v02.api.letsencrypt.org/directory"
dnsChallenge:
provider: azuredns
Finally you put anything else into the ./services/
folder; in your case it could be the definition of your pre-generated certificates for your local domain.
./services/localCertificates.yml
tls:
certificates:
- certFile: /certs/bundle.crt
keyFile: /certs/bundle.key
stores: default
The only downside is that changes to the traefik.yml
static configuration require the container to be forcefully restarted, as docker compose up -d
won't detect any changes (i.e. need to add --force-recreate
to the command).
PS. Since I don't expose port 8080 in my docker-compose
I can probably simplify the config a bit more, since I would also not need the insecure: true
definition. In that case I should probably use a more dedicated setup for Traefik's panel.
1 Like
Have a look at simple Traefik example. It will handle TLS, route by sub-domain, use labels on target service to configure.
Just duplicate whoami
for the services. Make sure to use a different router names in the labels for each service.