**Purpose**: A traefik reverse proxy is a server that sits between your network firewall and servers hosting various web services on your private network(s).  Traefik automatically handles the creation of Let's Encrypt SSL certificates if you have a domain registrar that is supported by Traefik such as CloudFlare; by leveraging API keys, Traefik can automatically make the DNS records for Let's Encrypt's DNS "challenges" whenever you add a service behind the Traefik reverse proxy.

!!! info "Assumptions"
    This Traefik deployment document assumes you have deployed [Portainer](https://docs.bunny-lab.io/Docker %26 Kubernetes/Servers/Docker/Portainer/) to either a Rocky Linux or Ubuntu Server environment.  Other docker-compose friendly operating systems have not been tested, so your mileage may vary regarding successful deployment ouside of these two operating systems.

    Portainer makes deploying and updating Traefik so much easier than via a CLI.   It's also much more intuitive.

## Deployment on Portainer
- Login to Portainer (e.g. https://<portainer-ip>:9443)
- Navigate to "**Environment (usually "local") > Stacks > "+ Add Stack"**"
- Enter the following `docker-compose.yml` and `.env` environment variables into the webpage
- When you have finished making adjustments to the environment variables (and docker-compose data if needed), click the "**Deploy the Stack**" button

!!! warning "Get DNS Registrar API Keys BEFORE DEPLOYMENT"
    When you are deploying this container, you have to be mindful to set valid data for the environment variables related to the DNS registrar.  In this example, it is CloudFlare.

    ```jsx title="Environment Variables"
    CF_API_EMAIL=nicole.rappe@bunny-lab.io
    CF_API_KEY=REDACTED-CLOUDFLARE-DOMAIN-API-KEY
    ```

    If these are not set, Traefik will still work, but SSL certificates will not be issued from Let's Encrypt, and SSL traffic will be terminated using a self-signed Traefik-based certificate, which is only good for local non-production testing.

    If you plan on using HTTP-based challenges, you will need to make the following changes in the docker-compose.yml data:
    
    - Un-comment `"--certificatesresolvers.myresolver.acme.tlschallenge=true"`
    - Comment-out `"--certificatesresolvers.letsencrypt.acme.dnschallenge=true"`
    - Comment-out `"--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"`  
    - Lastly, you need to ensure that port 80 on your firewall is opened to the IP of the Traefik Reverse Proxy to allow Let's Encrypt to do TLS-based challenges.

### Stack Deployment Information
```jsx title="docker-compose.yml"
version: "3.3"
services:
  traefik:
    image: "traefik:latest"
    restart: always
    container_name: "traefik-bunny-lab-io"
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
    labels:
      - "traefik.http.routers.traefik-proxy.middlewares=my-buffering"
      - "traefik.http.middlewares.my-buffering.buffering.maxRequestBodyBytes=104857600"
      - "traefik.http.middlewares.my-buffering.buffering.maxResponseBodyBytes=104857600"
      - "traefik.http.middlewares.my-buffering.buffering.memRequestBodyBytes=2097152"
      - "traefik.http.middlewares.my-buffering.buffering.memResponseBodyBytes=2097152"
      - "traefik.http.middlewares.my-buffering.buffering.retryExpression=IsNetworkError() && Attempts() <= 2"
    command:
      # Globals
      - "--log.level=ERROR"
      - "--api.insecure=true"
      - "--global.sendAnonymousUsage=false"
      # Docker
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      # File Provider
      - "--providers.file.directory=/etc/traefik/dynamic"
      - "--providers.file.watch=true"

      # Entrypoints
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure" # Redirect HTTP to HTTPS
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https" # Redirect HTTP to HTTPS
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true" # Redirect HTTP to HTTPS
      # LetsEncrypt
###      - "--certificatesresolvers.myresolver.acme.tlschallenge=true" # Enable if doing Port 80 Let's Encrypt Challenges
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" # Disable if doing Port 80 Let's Encrypt Challenges
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare" # Disable if doing Port 80 Let's Encrypt Challenges
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"

      # Keycloak plugin configuration
      - "--experimental.plugins.keycloakopenid.moduleName=github.com/Gwojda/keycloakopenid" # Optional if you have Keycloak Deployed
      - "--experimental.plugins.keycloakopenid.version=v0.1.34" # Optional if you have Keycloak Deployed
      
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/srv/containers/traefik/letsencrypt:/letsencrypt"
      - "/srv/containers/traefik/config:/etc/traefik"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "/srv/containers/traefik/cloudflare:/cloudflare"
    networks:
        docker_network:
          ipv4_address: 192.168.5.29
    environment:
      - CF_API_EMAIL=${CF_API_EMAIL}
      - CF_API_KEY=${CF_API_KEY}
    extra_hosts:
      - "mail.bunny-lab.io:192.168.3.13" # Just an Example

networks:
  default:
    external:
      name: docker_network
  docker_network:
    external: true

```

```jsx title=".env"
CF_API_EMAIL=nicole.rappe@bunny-lab.io
CF_API_KEY=REDACTED-CLOUDFLARE-DOMAIN-API-KEY
LETSENCRYPT_EMAIL=nicole.rappe@bunny-lab.io
```

!!! info
    There is a distinction between the "Global API Key" and a "Token API Key".  The main difference being that the "Global API Key" can change anything in Cloudflare, while the "Token API Key" can only change what it was granted delegated permissions to.

## Adding Servers / Services to Traefik
Traefik operates in two ways, the first is labels, while the second are dynamic configuration files.  We will go over each below.

### Docker-Compose Labels 
The first is that it reads "labels" from the docker-compose file of any deployed containers on the same host as Traefik.  These labels typically look something like the following:

```jsx title="docker-compose.yml"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gitea.rule=Host(`example.bunny-lab.io`)"
      - "traefik.http.routers.gitea.entrypoints=websecure"
      - "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
      - "traefik.http.services.gitea.loadbalancer.server.port=8080"
```

By adding these labels to any container on the same server as Traefik, traefik will automatically "adopt" this service and route traffic to it as well as assign an SSL certificate to it from Let's Encrypt.  The only downside is as mentioned above, if you are dealing with something that is not just a container, or maybe a container on a different physical server, you need to rely on dynamic configuration files, such as the one seen below.

### Dynamic Configuration Files
Dynamic configuration files exist under the Traefik container located at `/etc/traefik/dynamic`.  Any `*.yml` files located in this folder will be hot-loaded anytime they are modified.  This makes it convenient to leverage something such as the [Git Repo Updater](https://docs.bunny-lab.io/Docker%20%26%20Kubernetes/Docker/Custom%20Containers/Git%20Repo%20Updater/) container to leverage [Gitea](https://docs.bunny-lab.io/Docker%20%26%20Kubernetes/Docker/Docker%20Compose/Gitea/) to push configuration files from Git into the production environment, saving yourself headache and enabling version control over every service behind the reverse proxy.

An example of a dynamic configuration file would look something like this:

```jsx title="/etc/traefik/dynamic/example.bunny-lab.io.yml"
http:
  routers:
    example:
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
      http2:
      service: example
      rule: Host(`example.bunny-lab.io`)

  services:
    example:
      loadBalancer:
        servers:
          - url: http://192.168.5.70:8080
        passHostHeader: true
```

You can see the similarities between the labeling method and how you designate the proxy name `example.bunny-lab.io` the internal ip address `192.168.5.70` the protocol to request the data from the service internally `http`, and the port the server is listening on internally `8080`.  If you want to know more about the parameters such as `passHostHeader: true` then you will need to do some of your own research into it.

!!! example "Service Naming Considerations"
    When you deploy a service into a Traefik-based reverse proxy, the name of the `router` and `service` have to be unique.  The router can have the same name as the service, such as `example`, but I recommend naming the services to match the FQDN of the service itself.

    For example, `remote.bunny-lab.io` would be written as `remote-bunny-lab-io`.  This keeps things organized and easy to read if you are troubleshooting things in Traefik's logs or webUI.  The complete configuration file would look like the example below:

    ```jsx title="/etc/traefik/dynamic/remote.bunny-lab.io.yml"
    http:
      routers:
        remote-bunny-lab-io:
          entryPoints:
            - websecure
          tls:
            certResolver: letsencrypt
          http2:
          service: remote-bunny-lab-io
          rule: Host(`remote.bunny-lab.io`)

      services:
        remote-bunny-lab-io:
          loadBalancer:
            servers:
              - url: http://192.168.5.70:8080
            passHostHeader: true
    ```