Skip to Content

Traefik Setup - The Foundation

This foundation Traefik setup will enable hosting a wide range of containerized and non-containerized services.
August 10, 2026 by
Alixsander Haj Saw


Introduction

As of this writing, I've been using this setup on a single server to host n8n, pure html sites (you'd be surprised how much you can do with just html), Wordpress, Odoo, Plausible, and JS apps. Traefik makes it easy to forward traffic to the mentioned backend services using a simple onetime setup that I will cover bellow.


Traefik VS Nginx

You might wonder, how does Ngxin compare with Traefik, here are the main differences between both:

  • Traefik setup is much simpler than Nginx, especially for containers.
  • Traefik comes built in with SSL certificate generation/renewal mechanism, you do not need any additiona setup and configuration like Nginx with Certbot.
  • Traefik is a reverse proxy/router only, while Nginx works as a web server as well, therefor Traefik cannot server files like Nginx does, therefor if you need to server files, you'd need a web server with the Traefik setup.


Prerequisites

  • Docker + Docker Compose installed
  • A domain with a DNS record pointing at this server's public IP
  • Ports 80 and 443 free and reachable from the internet (needed for the Let's Encrypt HTTP challenge)


Directory and Docker Compose Setup 

This example is suitable for an Ubuntu setup, create the appropriate directory for your docker projects and clone the github repo:

sudo mkdir /opt/docker
sudo chown <your user>:<your user> /opt/docker
cd /opt/docker
git clone https://github.com/opensourcehustle/traefik.git

Setup

1. Create the external network (other stacks will join this to be routed by Traefik):

docker network create traefik-network

2. Copy the env template and fill it in:

cp .env.example .env

Fill in:

VariableWhat it is
DOMAINHostname for the Traefik dashboard, e.g. traefik.yourdomain.com
TZServer timezone, e.g. Europe/London
TRAEFIK_AUTHDashboard login — see step 3
TRAEFIK_ALLOWED_IPSCIDR ranges allowed to reach the dashboard

3. Generate a dashboard password hash:

sudo apt install apache2-utils   # provides htpasswd, if not already installed
htpasswd -nB admin

Paste the full output (e.g. admin:$2y$05$...) into TRAEFIK_AUTH in .env. Double any currency signs, otherwise it is considered a variable, in the admin:$2y$05$... eg, it should be updated to admin:$$2y$$05$$...

4. Set your Let's Encrypt email:

Open traefik.yml and replace CHANGE_ME@yourdomain.com under certificatesResolvers.letsencrypt.acme.email with your real address. (This one lives in traefik.yml rather than .env — Traefik's static config file isn't env-substituted the way the compose labels are.)

5. Create acme.json with the right permissions:

Let's Encrypt refuses to store certs in a file with overly permissive access.

touch acme.json
chmod 600 acme.json

6. Start it:

docker compose up -d
docker compose logs -f traefik

Once it's up, visit https://<DOMAIN> — you should get a cert automatically and be prompted for the dashboard login.

Adding another service behind this proxy

Any container joined to traefik-network with the right labels gets picked up automatically. Example:

services:
  myapp:
    image: myapp:latest
    networks:
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myapp.rule=Host(`myapp.yourdomain.com`)"
      - "traefik.http.routers.myapp.entrypoints=websecure"
      - "traefik.http.routers.myapp.tls.certresolver=letsencrypt"

networks:
  traefik-network:
    external: true

For services that aren't Docker containers on this host, use the file provider instead — see config/dynamic-example.yml.dis


Serving Static Files Example

Lets serve regular HTML files using Traefik+Nginx. Make sure you have your domain pointing to your Traefik server. 

Lets start by creating the appropriate directory:

mkdir /opt/docker/html-example && cd /opt/docker/html-example

Now lets create our compose file:

nano compose.yml

Add the following directives to it:

services:
nginx:
image: nginx:alpine
container_name: html-nginx
restart: unless-stopped
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- traefik-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.yourdomain.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.yourdomain.entrypoints=websecure"
- "traefik.http.routers.yourdomain.tls.certresolver=letsencrypt"
- "traefik.http.services.yourdomain.loadbalancer.server.port=80"

networks:
traefik-network:
external: true

Replace "yourdomain" with your actual domain.

Now start the container and Traefik will handle everything:

sudo docker compose up -d

This should create the default nginx directories, now lets add our own html to the html directory:

cd html

Now lets download a ready template from tooplate.com:

sudo wget https://www.tooplate.com/zip-templates/2165_neon_carbon.zip

Lets install unzip and unzip the directory:

sudo apt install unzip && sudo unzip 2165_neon_carbon.zip

Now lets move the files and delete unnecessary directories:

sudo mv 2165_neon_carbon/* .
sudo rm 2165_neon_carbon.zip
sudo rm -rf 2165_neon_carbon

Lets also add our nginx configuration as well:

cd ../nginx
sudo nano default.conf

Add the following directives to it:

server {
listen 80;
server_name _;

root /usr/share/nginx/html;
index index.html;

error_page 404 /404.html;

location = /404.html {
internal;
}

location / {
try_files $uri $uri/ =404;
}
}

Lets down and up the nginx container:

sudo docker compose down
sudo docker compose up -d

Now visit your site, it should be accessible over https and serving our fancy html files.