Discourse with Traefik 2.0

yeah why would you want the discourse container to manage your ssl-certificates, if you are about to enable a proxy infront?

please post your solution as soon as you are done.

in Running other websites on the same machine as Discourse see

I would suggest do add the template section in the docs as well. I noticed that it was not commented in my install, as I did standalone first to get the app.yml. In the very beginning, I forgot to comment them. Could avoid that others run in the same problem.

for sure

it doesn’t show the static configuration of Traefik, does it?

Good News Everyone ! http://forum.fairbnb.community/ is reachable.

This is the configuration with minimum changes in app.yml to have it served via http, I will dive into https on the weekend.

templates:
    - "templates/postgres.template.yml"
    - "templates/redis.template.yml"
    - "templates/web.template.yml"
    - "templates/web.ratelimited.template.yml"
    ## Uncomment these two lines if you wish to add Lets Encrypt (https)
    #- "templates/web.ssl.template.yml"
    #- "templates/web.letsencrypt.ssl.template.yml"  

expose:
      #- "80:80"   # http
      #- "443:443" # https

labels:
    traefik.enable:                                               true
    traefik.http.services.discourse.loadbalancer.server.port:     80
    traefik.http.routers.discourse.rule:                          Host(`forum.fairbnb.community`)
    traefik.http.routers.discourse.entrypoints:                   web

For SSL just configure Traefik + Let’s Encrypt, you don’t need to use discourse ssl.

Traefik will mange encryption over all of yours containers

Currently i’m using traefik 1.6 so i don’t now yet how to enable it on traefik v2

yep , see Running other websites on the same machine as Discourse but it looks like that discourse expect certain header information when the client speaks to proxy via https.

I’ve briefly read into it. I’ll try to translate in text understandable by the nine-to-five user.

yep, see also in https://docs.traefik.io/https/overview. In the beginning, I started with https straight away and certification creation went successful but that is it. Now I have to align it with the knowledge I have gained since then.

So you use https to reach your forum, don’t you?
Can you share the config of Traefik and Discourse? The more is shared the easier it gonna be for me on the weekend.
V1 to v2 guidance is given in Traefik v1 to v2 - Traefik but I try to have it explained here on the weekend.

OK this is my traefik.toml, not valid for new version v2

defaultEntryPoints = ["http", "https"]

[web]
address = ":8080"
  [web.auth.basic]
  users = ["yourusername:yourpassword"]

[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
      entryPoint = "https"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]

[acme]
email = "XXXXXXXX"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
onDemand = false
[acme.httpChallenge]
entryPoint = "http"
minVersion = "VersionTLS12"

part of Discourse Config

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"

expose:
  - "80"   # http ------ just espose container ports 
  - "443" # https ------ you don't need to expose external host port

labels:
  traefik.port: '80'
  traefik.backend: 'mycontainername'
  traefik.frontend.rule: 'Host:www.myhost.com'
  traefik.docker.network: 'mynetworkname'
docker_args:
  - "--network=mynetworkname"

with this config traefik redirect all request from port 80 “http” to port 443 “https”,
enable FORCE HTTPS in discourse admin panel to avoid errors.

1 Like

@pfaffman, I don’t know if notifcation still works, if I quote you reply in another topic, so I tag you.

So your config looks like the one above but

and using Environment variables (this approach is referenced in How do you set environmental variables for Traefik? - Stack Overflow and https://blog.raveland.org/post/traefik_compose)

so https works :partying_face: :partying_face: :partying_face: :partying_face: :partying_face:
and I think I understood how that all works :slight_smile:. I will try to write it down the comings days

my config

discourse app.yml extract

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

## which TCP/IP ports should this container expose?
## If you want Discourse to share a port with another webserver like Apache or nginx,
## see https://meta.discourse.org/t/17247 for details
expose:
  #- "80:80"   # http
  #- "443:443" # https
  #- "80"      # http
  #- "443"     # https
  #- "40080:80"
  #- "40443:443"

labels:
  app_name:                                                                     discourse

  #----Traefik lables------------------------
  traefik.enable:                                                               true
  traefik.docker.network:                                                       bridge_proxy_traefikv2

   #---HTTP ROUTER SECTION-------------------
  traefik.http.routers.discourse.rule:                                          Host(`forum.fairbnb.community`)
    #--HTTP SECTION--------------------------
  traefik.http.routers.discourse.entrypoints:                                   web
  traefik.http.routers.discourse.middlewares:                                   discourse_redirect2https         
  #traefik.http.services.discourse.loadbalancer.server.port:                     80  

   #---HTTPS ROUTER SECTION
  traefik.http.routers.discourse_secure.rule:                                   Host(`forum.fairbnb.community`)
    #--HTTPS SECTION
  traefik.http.routers.discourse_secure.entrypoints:                            websecure
  traefik.http.services.discourse_secure.loadbalancer.server.port:              80
    #--TLS SECTION
  traefik.http.routers.discourse_secure.tls.certresolver:                       tlsChallenge_letsencrypt

   #---MIDDLEWARE SECTION redirect http to https
  traefik.http.middlewares.discourse_redirect2https.redirectscheme.scheme:      https

docker_args:
  - "--network=bridge_proxy_traefikv2"
  
params:

Treafik Static Config

global:
  checkNewVersion: true
entryPoints:
  web:
    address: :80
  websecure:
    address: :443

api:
   debug: true

providers:
  docker:
    exposedByDefault: false
    network: bridge_proxy_traefikv2

log:
  level: DEBUG
  filePath: /var/log/traefik-log.log

accessLog:
  filePath: /var/log/traefik-access.log

certificatesResolvers:
  tlsChallenge_letsencrypt:
    acme:
      email: my.secret@gmail.com
      storage: /etc/ssl/certs/letsencrypt/acme.json
      tlsChallenge: {}
3 Likes

Greetings,

I used this post as a guide for setting up Discourse to work with Traefik. I have traefik working with another web app.

When I visit the forum.private .com , I get a “404 page not found” error.

Something seems to be working, because I in the Traefik dashboard in the services tab, I can see a discourse@docker and discourse_secure@docker

However, there is nothing for discourse in the routers tab.

The changes I have made to app.yml are here, largely based on the post linked above. I added an an exposed port on the docker arguments at the bottom which seemed to open up the services mentioned above. I’d appreciate any help!

Traefik docker-compose
version: "3.3"

services:
  ################################################
  ####        Traefik Proxy Setup           #####
  ###############################################
  traefik:
    image: traefik:v2.0
    restart: always
    container_name: traefik
    ports:
      - "80:80" # <== http
      - "8080:8080" # <== :8080 is where the dashboard runs on
      - "443:443" # <== https
    command:
    #### These are the CLI commands that will configure Traefik and tell it how to work! ####

      - --api.insecure=true # <== Enabling insecure api, NOT RECOMMENDED FOR PRODUCTION
      - --api.dashboard=true # <== Enabling the dashboard to view services, middlewares, routers, etc...
      - --api.debug=true # <== Enabling additional endpoints for debugging and profiling
      ## Log Settings (options: ERROR, DEBUG, PANIC, FATAL, WARN, INFO) - https://docs.traefik.io/observability/logs/ ##
      - --log.level=DEBUG # <== Setting the level of the logs from traefik
      ## Provider Settings - https://docs.traefik.io/providers/docker/#provider-configuration ##
      - --providers.docker=true # <== Enabling docker as the provider for traefik
      - --providers.docker.exposedbydefault=false # <== Don't expose every container to traefik, only expose enabled ones
      - --providers.file.filename=/dynamic.yaml # <== Referring to a dynamic configuration file
      - --providers.docker.network=web # <== Operate on the docker network named web
      ## Entrypoints Settings - https://docs.traefik.io/routing/entrypoints/#configuration ##
      - --entrypoints.web.address=:80 # <== Defining an entrypoint for port :80 named web
      - --entrypoints.web-secured.address=:443 # <== Defining an entrypoint for https on port :443 named web-secured
      ## Certificate Settings (Let's Encrypt) -  https://docs.traefik.io/https/acme/#configuration-examples ##
      - --certificatesresolvers.mytlschallenge.acme.tlschallenge=true # <== Enable TLS-ALPN-01 to generate and renew ACME certs
      - --certificatesresolvers.mytlschallenge.acme.email=private@private.com # <== Setting email for certs
      - --certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json # <== Defining acme file to store cert information
    volumes:
      - ./letsencrypt:/letsencrypt # <== Volume for certs (TLS)
      - /var/run/docker.sock:/var/run/docker.sock # <== Volume for docker admin
      - ./dynamic.yaml:/dynamic.yaml # <== Volume for dynamic conf file, **ref: line 27
    networks:
      - web # <== Placing traefik on the network named web, to access containers on this network
    labels:
    #### Labels define the behavior and rules of the traefik proxy for this container ####
      - "traefik.enable=true" # <== Enable traefik on itself to view dashboard and assign subdomain to view it
      - "traefik.http.routers.api.rule=Host(`monitor.private.com`)" # <== Setting the domain for the dashboard
      - "traefik.http.routers.api.service=api@internal" # <== Enabling the api to be a service to access


networks:
  web:
    external: true
  backend:
    external: false

volumes:
  db_data: {}
  wordpress:
    external: true
  db:
    external: true

Discourse app.yml
## this is the all-in-one, standalone Discourse Docker container template
##
## After making changes to this file, you MUST rebuild
## /var/discourse/launcher rebuild app
##
## BE *VERY* CAREFUL WHEN EDITING!
## YAML FILES ARE SUPER SUPER SENSITIVE TO MISTAKES IN WHITESPACE OR ALIGNMENT!
## visit http://www.yamllint.com/ to validate this file as needed

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  # "templates/web.letsencrypt.ssl.template.yml"

## which TCP/IP ports should this container expose?
## If you want Discourse to share a port with another webserver like Apache or nginx,
## see https://meta.discourse.org/t/17247 for details
expose:
  #- "80:80"   # http
  #- "443:443" # https

params:
  db_default_text_search_config: "pg_catalog.english"

  ## Set db_shared_buffers to a max of 25% of the total memory.
  ## will be set automatically by bootstrap based on detected RAM, or you can override
  db_shared_buffers: "128MB"

  ## can improve sorting performance, but adds memory usage per-connection
  #db_work_mem: "40MB"

  ## Which Git revision should this container use? (default: tests-passed)
  #version: tests-passed

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en

  ## How many concurrent web requests are supported? Depends on memory and CPU cores.
  ## will be set automatically by bootstrap based on detected CPUs, or you can override
  UNICORN_WORKERS: 2

  ## TODO: The domain name this Discourse instance will respond to
  ## Required. Discourse will not work with a bare IP number.
  DISCOURSE_HOSTNAME: forum.private.com

  ## Uncomment if you want the container to be started with the same
  ## hostname (-h option) as specified above (default "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: List of comma delimited emails that will be made admin and developer
  ## on initial signup example 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'private@private.com'

  ## TODO: The SMTP mail server used to validate new accounts and send notifications
  # SMTP ADDRESS, username, and password are required
  # WARNING the char '#' in SMTP password can cause problems!
  DISCOURSE_SMTP_ADDRESS: in-v3.mailjet.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: redacted
  DISCOURSE_SMTP_PASSWORD: "redacted"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)

  ## If you added the Lets Encrypt template, uncomment below to get a free SSL certificate
  LETSENCRYPT_ACCOUNT_EMAIL: private@private.com

  ## The http or https CDN address for this Discourse instance (configured to pull)
  ## see https://meta.discourse.org/t/14857 for details
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

## The Docker container is stateless; all data is stored in /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## Plugins go here
## see https://meta.discourse.org/t/19157 for details
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Any custom commands to run after building
run:
  - exec: echo "Beginning of custom commands"
  ## If you want to set the 'From' email address for your first registration, uncomment and change:
  ## After getting the first signup email, re-comment the line. It only needs to run once.
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  - exec: echo "End of custom commands"

labels:
  app_name:                                                                     discourse

  #----Traefik lables------------------------
  traefik.enable:                                                               true
  traefik.docker.network:                                                       web

   #---HTTP ROUTER SECTION-------------------
  traefik.http.routers.discourse.rule:                                          Host(`forum.private.com`)
    #--HTTP SECTION--------------------------
  traefik.http.routers.discourse.entrypoints:                                   web
  traefik.http.routers.discourse.middlewares:                                   discourse_redirect2https         
  traefik.http.services.discourse.loadbalancer.server.port:                     80  

   #---HTTPS ROUTER SECTION
  traefik.http.routers.discourse_secure.rule:                                   Host(`forum.private.com`)
    #--HTTPS SECTION
  traefik.http.routers.discourse_secure.entrypoints:                            web-secured
  traefik.http.services.discourse_secure.loadbalancer.server.port:              80
    #--TLS SECTION
  traefik.http.routers.discourse_secure.tls.certresolver:                       tlsChallenge_letsencrypt

   #---MIDDLEWARE SECTION redirect http to https
  traefik.http.middlewares.discourse_redirect2https.redirectscheme.scheme:      https

docker_args:
  - "--network=web"
  - "--expose=80"

Thanks

can you share screenshots?

Attached are two screenshots from the traefik dashboard. They show that discourse is showing up (twice) in the service, but nothing in the routers. I don’t necessarily know that means TBH but I noticed it.

Edit: I am a new discourse user so I am not trusted to put two images in one post, so I will reply with another.

Need to bring up my dockers tonight to compare, let you know than or maybe some others here can share their dashboard and compare

you don’t need that, better switch to the API and use traefik v2.1, so:
- "traefik.http.routers.traefik_dashboard-router.service=api@internal"
See Endless 502 / forwarding when calling dashboard via subdomain #6123 - Traefik v2 - Containous Community Forum

can you remove your comments

it make it easier to read for us.
No worries I was told the same once :).
I write my comments in extra line, so I can create a clean print easily using cat traefikV2.yaml | grep -v "#"

here is my dashboard

  1. Dashboard
  2. HTTP Routers
  3. HTTP Services
  4. HTTP Middlewares

Okay, took me a few days to get to this. I have redone my Traefik configuration to use a yaml file instead of putting it all in docker-compose. However, after wiring everything up again, I seem to get the same or similar behavior – I am getting a 404 at my domain, and in the Traefik dashboard I see entrie under Services and routers for discourse, but nothing under routers.

Traefik docker-compose
version: '3'

services:
  traefik:
    image: traefik:v2.0
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`monitor.example.com`)"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=user:redacted"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`monitor.example.com`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=http"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true
Traefik data/traefik.yml
api:
  dashboard: true

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  http:
    acme:
      email: nick@innomadic.com
      storage: acme.json
      httpChallenge:
        entryPoint: http
containers/app.yml
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"

expose:

params:
  db_default_text_search_config: "pg_catalog.english"

  db_shared_buffers: "128MB"



env:
  LANG: en_US.UTF-8

  UNICORN_WORKERS: 2

  DISCOURSE_HOSTNAME: forum.example.com


  DISCOURSE_DEVELOPER_EMAILS: 'info@example.com'

  DISCOURSE_SMTP_ADDRESS: redacted.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: redacted
  DISCOURSE_SMTP_PASSWORD: "redacted"

  LETSENCRYPT_ACCOUNT_EMAIL: info@example.com


volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

run:
  - exec: echo "Beginning of custom commands"
  - exec: echo "End of custom commands"

labels:
  app_name:                                                                     discourse

  traefik.enable:                                                               true
  traefik.docker.network:                                                       proxy
  traefik.http.routers.discourse.rule:                                          Host(`forum.example.com`)
  traefik.http.routers.discourse.entrypoints:                                   http
  traefik.http.routers.discourse.middlewares:                                   discourse_redirect2https
  traefik.http.services.discourse.loadbalancer.server.port:                     80

  traefik.http.routers.discourse_secure.rule:                                   Host(`forum.example.com`)
  traefik.http.routers.discourse_secure.entrypoints:                            https

  traefik.http.services.discourse_secure.loadbalancer.server.port:              80
  traefik.http.routers.discourse_secure.tls.certresolver:                       tlsChallenge_letsencrypt

  traefik.http.middlewares.discourse_redirect2https.redirectscheme.scheme:      https

docker_args:
  - "--network=proxy"
  - "--expose=80"

I think I ran the docker network connect proxy command as well to attach this to the traefik network.

I appreciate any help to see what I’m missing here, and also I would love to hear if I have my security ducks in a row.

I don’t know what you’re missing. Here’s what I do in Ansible to crank up sites with traefik:

        --docker-args "-l traefik.frontend.rule=Host:{{discourse_hostname}} \
        -l traefik.frontend.entryPoints=https \
        -l traefik.backend={{discourse_shortname}} \
        -l traefik.port=80"

And then do

       ./launcher start {{ discourse_yml }} {{ docker_args }}

Adding the traefik rules in docker_args rather than in the yml has the added benefit of not having traefik pay attention to the container that’s getting bootstrapped.

I think that is Traefik v1 maybe?

Oh. Sorry. Yeah, I’m pretty sure that’s traefik 1, so I’m not going to be any help with specifics.

It doesn’t look like you’re setting any values in those labels in app.yml? I think that you need to set a rule and, maybe a middleware?

If you won’t want down time during a bootstrap you’ll want to set them with ./launcher start as in my example.

Maybe “middleware” is what used to be “backend”? You’ll need to do something to establish that the Discourse container is the server you want and . . . something else . . . to connect some front end/url with the appropriate backend/server.