Traefik reverse proxy
How to setup traefik as a reverseproxy with letsencrypt.
Context
I have previously worked with nginx proxy manager and i liked it. However i wanted SSO auth as middleware and that was to complicated with nginx proxy manager. Hence i shiftet to Traefik. Which might be a steep learning curve, however when you get the grip on it. It is quite easy.
The first thing to have in mind is labeling and the difference between static and dynamic files. The way i do it, is that i have Traefik running on a network, where if i put the container on that network i intent to expose it to Traefik.
Also i have it working with letsencrypt and cloudflare as dns provider.
Setup
First make sure to have docker installed. guide for installing docker and docker compose: Install guide
I like to have a directory specific for docker like /etc/docker/
-
create a working dir for Traefik.
- create a compose file for Traefik:
1
touch docker-compose.yml
-
Edit the file (i use nano in termial or vscode) do NOT run the container yet.
Compose Stack:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
networks:
traefik-proxy:
name: traefik-proxy
services:
traefik:
image: traefik:v3.6.5
container_name: traefik
restart: unless-stopped
command:
- --providers.file.filename=/traefik.yml
- --log.level=debug
- --certificatesresolvers.le.acme.tlschallenge=true
environment:
- TZ=Europe/Copenhagen # Change this to your timezone
- CF_DNS_API_TOKEN=<cloudflare token>
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./acme.json:/acme.json
- ./traefik.yml:/traefik.yml
- ./dynamic:/dynamic
networks:
- traefik-proxy
labels:
- traefik.enable=true
# Route dashboard ONLY through traefik.home.labify.dk
- traefik.http.routers.traefik.rule=Host(`<sub.domain.local>`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls.certResolver=le
# Enable Traefik internal API as a service
- traefik.http.routers.traefik.service=api@internal
##Test container##
hello-world:
image: kerwood/hello-world:latest
container_name: hello-world
restart: unless-stopped
expose:
- 3000
networks:
- traefik-proxy
labels:
- traefik.enable=true ##For Traefik to be able to read the labels
- traefik.http.services.hello.loadbalancer.server.port=3000 ## port that the service runs on
- traefik.http.routers.hello.rule=Host(`sub.domain.local`) ## domain to test
- traefik.http.routers.hello.tls.certResolver=le ## which certificate resolver to use, see traefik.yml for resolvers
- traefik.http.routers.hello.entrypoints=websecure ## entrypoint, see traefik.yml for explanation (443)
As we can see in the stack, there are som directories and files we need to make.
- first we need to create acme.json, this is importent as Traefik tries to create a directory and not a file ( my own experience, which is odd)
Traefik will complain about permissions if these are wrong.
1
2
touch acme.json
chmod 600 acme.json
Nothing more is needed for the acme.json file.
- Now we need to create the traefik.yml file
1
touch traefik.yml
edit the file so it contains:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
api:
dashboard: true # Optional can be disabled
insecure: false # Optional can be disabled
debug: true # Optional can be Enabled if needed for troubleshooting
entryPoints:
web: ##namig is important here, since this name is used in the labels
address: ":80"
# Optional if you want to redirect all HTTP to HTTPS
http:
redirections:
entryPoint:
to: websecure
websecure: ##namig is important here, since this name is used in the labels
address: ":443"
#serversTransport:
# insecureSkipVerify: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
watch: true
# network: proxy # Optional; Only use the "proxy" Docker network, even if containers are on multiple networks.
file:
directory: /dynamic
watch: true
certificatesresolvers:
le: ##namig is important here, since this name is used in the labels
acme:
email: <valid email>
storage: /acme.json
caServer: https://acme-v02.api.letsencrypt.org/directory # prod (default) they have a staging url also.
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 0
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
nothing more needs to be done with that file
- create the dynamic dir, this is used for hosts that are not docker (ip:port redirects)
1 2
mkdir dynamic cd dynamic
now create the redirects you need example file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http:
routers:
<name>:
rule: "Host(`sub.domain.local)"
service: gitea
entryPoints:
- websecure # or web if using HTTP
tls: # optional, if using HTTPS
certResolver: le ## name from traefik.yml
services:
<name>: #must be same as above
loadBalancer:
servers:
- url: "http://ip:port"
- Remeber to correct your dns to point to the sub.domain.local you have used. I have used prod.domain.local or test.domain.local and then subsub domains for each container. point the “main” domain to the ip that traefik runs on. like:
*.prod.domain.local IP
this is supported by most, if they are not consider changing your dns resolver. Hint 8.8.8.8 as resolver will work as long as your personal resolver (cloudflare or similar) points *.prod.domain.local to your wan.
Test
Now the exiting part begins.
navigate to traefik dir. run:
1
docker compose up -d
you should be able to visit your traefik dashboard and hello server know. if not run:
1
docker logs traefik
Correct mistakes.
Considerrations:
Have your dashboard behind middleware see oauth2proxy or smallsteps mtls (comming soon)