In the previous post Traefik was compared to NGINX. Now let’s take a look at a few simple Traefik setups. We’ll focus on specifying the host and port.
NGINX Server on 127.0.0.1
Below is a docker-compose.yml
file which specifies two services:
web
andtraefik
.
The web
service uses NGINX to serve a simple HTML page (shared with the container via a volume mount).
version: '3'
services:
web:
image: nginx:alpine
container_name: nginx
labels:
- "traefik.http.routers.web.rule=Host(`localhost`)"
- "traefik.http.services.web.loadbalancer.server.port=80"
volumes:
- ./hello.html:/usr/share/nginx/html/index.html:ro
traefik:
image: traefik:v2.5
container_name: traefik
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
You’ll see a typical pattern in docker-compose.yml
files invoking Traefik:
- The Traefik service is started with a number of command line arguments specified via
command
. - The proxied service is started with a number of labels specified via
labels
(these are analogous toLABEL
in aDockerfile
but applied at run time rather than build time).
Let’s dissect the options passed to Traefik on the traefik
service:
--api.insecure=true
— Enable Traefik’s API and Dashboard on an insecure connection (HTTP). Useful for debugging or development environments, but for production environments, it’s recommended to use HTTPS and authentication.--providers.docker=true
— Enable the Docker provider, allowing Traefik to automatically discover and manage services based on Docker containers. Traefik listens to Docker daemon events, automatically detects containers and services, and applies routing rules based on their configuration (specified via labels).--entrypoints.web.address=:80
— Where Traefik receives requests. Defines an entrypoint namedweb
and configures it to listen on port 80, the standard port for HTTP traffic. Requests routed according to configured rules and discovered services.
The connection from Traefik to NGINX is set up by the labels on the web
service. Specifically:
traefik.http.routers.web.rule=Host(
localhost)
— Tells Traefik to create a routerweb
for HTTP traffic matching requests for the hostlocalhost
. Effectively tells Traefik to forward requests coming tolocalhost
to this service. In production,localhost
would be replaced with the actual domain names that the service is intended to respond to.traefik.http.services.web.loadbalancer.server.port=80
— Tells Traefik that the serviceweb
should be accessible through a load balancer on port 80. Traffic routed to this service should be forwarded to the internal port 80 of the container.
Finally you’ll note that the volume on the traefik
service connects the Docker socket on the host to the corresponding socket on the container. This is to allow Traefik to automatically discover Docker services.
Start the services with docker-compose up
and then go to http://localhost/ in a browser to see the HTML page.
It really could not be simpler than that! But now try http://127.0.0.1. This will give a 404. That’s because of the following rule that only routes requests to localhost
:
traefik.http.routers.web.rule=Host('localhost')
Dashboard
Traefik provides an interactive dashboard that can be found at http://127.0.0.1:8080/. 📌 You can also access it at http://locahost:8080/.
The dashboard is very useful for understanding what Traefik is doing and keeping track of how it dynamically scales services in response to infrastructure changes.
Python Server on Localhost
Let’s update the previous setup to also share the page at http://127.0.0.1.
version: '3'
services:
web:
image: python:alpine
container_name: web
command: /bin/sh -c "cd /usr/src/app && python -m http.server 80"
volumes:
- ./hello.html:/usr/src/app/index.html:ro
labels:
- "traefik.http.routers.web.rule=Host(`localhost`, `127.0.0.1`)"
- "traefik.http.services.web.loadbalancer.server.port=80"
traefik:
image: traefik:v2.5
container_name: traefik
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
To keep things fresh I have also replaced NGINX as the web server with the simple Python HTTP server.
Check http://127.0.0.1 again. It should work now (this has nothing to do with changing to the Python HTTP server and everything to do with the traefik.http.routers.web.rule
label).
Node Server on Domain Name
As a final example we’ll serve the page with a specific domain, www.example.com
.
version: '3'
services:
web:
image: node:alpine
container_name: web
command: /bin/sh -c "npm install -g http-server && http-server -p 80 /usr/src/app"
volumes:
- ./hello.html:/usr/src/app/index.html:ro
labels:
- "traefik.http.routers.web.rule=Host(`www.example.com`)"
- "traefik.http.services.web.loadbalancer.server.port=80"
traefik:
image: traefik:v2.5
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
Once again I have changed the service used to serve the page, now using Node rather than Python.
The above setup is equivalent to the following NGINX configuration, where server_name
is used to specify the domain on which the resource is being served.
server {
listen 80;
server_name www.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
The site is now set up to be served at http://www.example.com/. Of course, we don’t have access to the www.example.com domain, so we’ll need to fake it. One option would be to add an entry to /etc/hosts
:
127.0.0.1 www.example.com
With that you’ll be able to simply go to http://www.example.com in your browser. Alternatively, without modifying /etc/hosts
we can also specify name resolution as an option to curl
:
curl -v --resolve www.example.com:80:127.0.0.1 http://www.example.com/
* Added www.example.com:80:127.0.0.1 to DNS cache
* Hostname www.example.com was found in DNS cache
* Trying 127.0.0.1:80...
* Connected to www.example.com (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: www.example.com
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: max-age=3600
< Content-Length: 115
< Content-Type: text/html; charset=UTF-8
< Date: Sat, 02 Mar 2024 07:41:07 GMT
< Etag: W/"10756632-115-2024-03-02T07:14:39.476Z"
< Last-Modified: Sat, 02 Mar 2024 07:14:39 GMT
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
* Connection #0 to host www.example.com left intact
Conclusion
These three simple examples have show a selection of setups for using Traefik as a reverse proxy. Each setup has used a different approach to serving the content, including NGINX, Python and Node HTTP servers.