Discourse with Traefik 2.0

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 - #5 by ldez - Traefik v2 - Traefik Labs 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.

They are being set, but you have to scroll a bit to the right to see them. Just a formatting thing.

Thanks

1 Like

LOL. Sorry about that.

Well, I have no idea what I’m talking about, but it doesn’t make sense to me that you’d configure your discourse@docker as a load balancer.

Hey I think I got it.

I removed the line:
# traefik.http.services.discourse_secure.loadbalancer.server.port: 80

but left this one:

traefik.http.services.discourse.loadbalancer.server.port: 80

labels:
  app_name:                                                                     discourse

  #----Traefik lables------------------------
  traefik.enable:                                                               true
  traefik.docker.network:                                                       proxy
   #---HTTP ROUTER SECTION-------------------
  traefik.http.routers.discourse.rule:                                          Host(`forum.example.com`)
    #--HTTP SECTION--------------------------
  traefik.http.routers.discourse.entrypoints:                                   http
  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.example.com`)
    #--HTTPS SECTION
  traefik.http.routers.discourse_secure.entrypoints:                            https

 # 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

Not sure how I got that or why it is the way it is, but it works. So using all of the configuration I posted above in the thread, but making this one change, is working for me now.

Thanks all!

4 Likes

that is correct.
I put that in my config:

#—SERVICE SECTION tell treafik where to send the request
traefik.http.services.discourse.loadbalancer.server.port: 80

does that ring a bell?
What are you doing here is to tell to whom (service) the request is send to and at what port.
You could solve it also by using expose but than for a security perspective you want expose as little as possible of you containers. In addition, traefik picks the first port if yuo expose multiple ports, so using loadbalancer is cleaner


Traefik data/traefik.yml

I don’t do that as this path is the default path

you don’t work with the full path here, using an default path. I don’t like this because you have to keep in mind the path. I do storage: /etc/ssl/certs/letsencrypt/acme.json

I define the network, I don’t like default settings

providers:
  docker:
    exposedByDefault: false
    network: bridge_proxy_traefikv2

containers/app.yml

you don’t need to do this, this is done by loadbalancer

Traefik docker-compose

you may want to give this network a name, in my case

networks:
  traefik:
    external:
      name: bridge_proxy_traefikv2
1 Like

I’ve been working through your suggestions here. Thank you for your response.

I should point out that for the endpoint you say to trust the default and for the storage location you say you don’t like to trust defaults. :slight_smile:

I did try to make the change for acme.json you mentioned:

However, my traefik container did not find the file then. It showed up as an error in the log. So I have left it as acme.json for now.

I wanted to ask about this one though:

Aren’t you just renaming the network here, or giving it a localized name? I think I just referred to it as proxy in the other configuration and here locally. It still has a name that I defined, I am just not renaming it. At least that’s my understanding.

I have one final notable concern. I noticed after my “Success!” post above that although HTTPS is working, I am getting mixed content warning. So not everything is getting encrypted.

I am getting the mixed content error for my Discourse installation, but not the Wordpress installation I have on the same server. So it must be particular to my Discourse settings.

Looking in the developer console I see:

Loading mixed (insecure) display content “http://talk.redacted.com/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_180x180.png” on a secure page FaviconLoader.jsm:174:19

So maybe somehow my redirect is not working properly?

I am attaching relevant files below for review.

Traefik.yml

api:
dashboard: true

entryPoints:
http:
address: “:80”
https:
address: “:443”

providers:
docker:
exposedByDefault: false
network: proxy

certificatesResolvers:
http:
acme:
email: info@private.com
storage: acme.json
httpChallenge:
entryPoint: http

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.private.com)”
- "traefik.http.middlewares.traefik-auth.basicauth.users=private:private”
- “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.private.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

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: talk.private.com

DISCOURSE_DEVELOPER_EMAILS: ‘info@private.com’

LETSENCRYPT_ACCOUNT_EMAIL: info@private.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 GitHub - discourse/docker_manager: Plugin for use with discourse docker image

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(talk.private.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(talk.private.com)
traefik.http.routers.discourse_secure.entrypoints: https
traefik.http.routers.discourse_secure.tls: true
traefik.http.routers.discourse_secure.service: discourse
traefik.http.routers.discourse_secure.tls.certresolver: http

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

docker_args:

  • “–network=proxy”

So, any thoughts about why I’m getting the mixed content error?

Well, for a moment I thought discourse had become sentient because I got this notification in my admin view:

However, I made this change and still got the mixed content error. I also logged out, deleted cache and logged in again. Same thing.

It looks like my login requests are still encrypted, so that’s good, I guess, but I need to get ride of this mixed content error. Configuration is above in the previous post.

Another update: the mixed content warning is now gone. I’m not clear why. I guess that it’s related to the force_https setting, however, it I didn’t get a clean page load for maybe 30 minutes after I changed the setting, despite the fact that I cleared my cache and logged out and in, as I noted above.

The good news is I seem to have a functioning Discourse installation behind a Traefic reverse proxy.


it is the pics, what are sourced via http connections, had the same problem, see:


have you mounted the volume correctly, traefik can be quite confusing when it comes to the paths.
I do e.g.:

volumes:       
  - /etc/ssl/certs/traefik/letsencrypt:/etc/ssl/certs/letsencrypt
  - /opt/traefik/traefik-config.yaml:/etc/traefik/traefik.yaml
  - /etc/passwd.traefik.dashboard:/etc/passwd.BasicAuth.dashboard
  - /etc/passwd.traefik.whoami:/etc/passwd.BasicAuth.whoami

I cannot remember but there was a reason for it, maybe to see it well in the CLI when listing networks

No I did not mount like this. Here is is my volumes section from docker-compose for traefik:

    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

I notice that mine doesn’t even mention let’s encypt, whereas yours does. I guess you created that deeply nested folder in your docker-compose directory, and put your acme.json?

the other things I notice is that I have this docker/sock and localtime. Not sure what the localtime one is, and maybe the docker.sock statement is a security problem. Need to read up on that.

let’s encrypt cert creation is done by Traefik. Mounting a volume allows me to have it stored on the host instead of the docker
I recall that getting all the mounting done was a real hassle, did allo of checks doing docker exec -it

  • ls
  • dir
  • vi