Discourse with Caddy v2 (in Docker)

These examples assume the use of docker-compose to run Caddy. It’s possible to do this configuration other ways, in which case it can be adapted to your preferred method.

Socketed (Recommended)

This method is probably the simplest. It works for single and multisite Discourse installs in single and multiple container modes, including:


forum.example.com {
    reverse_proxy unix//sock/app/nginx.http.sock
Caddyfile for dual container multisite
forum.example.com, forum2.example.com, forum3.example.com {
    reverse_proxy unix//sock/web_only/nginx.http.sock

Discourse config (app.yml or web_only.yml):

  1. Add to templates section
  - "templates/web.socketed.template.yml"
  1. Comment out or remove SSL templates
  # - "templates/web.ssl.template.yml"
  # - "templates/web.letsencrypt.ssl.template.yml"
  1. Comment out or remove ports
  # - "80:80"   # http
  # - "443:443" # https
  1. Add a line to volumes in docker-compose (maps volumes to socket)
      - ${DISCOURSE_PATH}/shared:/sock
      # - /var/discourse/shared:/sock   # <---Default location
example docker-compose.yml
version: '3.3'

# DISCOURSE_PATH and CADDY_PATH come from .env in this example.
# Adjust them accordingly to your environment /var/www/caddy, etc.

    image: caddy
      - "80:80"
      - "443:443"
      - "80:80/udp"
      - "443:443/udp"
      - ${DISCOURSE_PATH}/shared:/sock
      - ${CADDY_PATH}/caddy/Caddyfile:/etc/caddy/Caddyfile
      - ${CADDY_PATH}/var/caddy:/root/.caddy
      - ${CADDY_PATH}/caddy/data:/data
      - ${CADDY_PATH}/caddy/config:/config
    restart: always


  1. The ./discourse-setup script requires ports 80 and 443 to be open and unassigned by Docker. So if you’re using that script, you’ll need to stop the Caddy container. After the script completes, you can switch to socketed, /launcher rebuild app, then restart the Caddy container.

Docker Ports (Alternate Method)

Arguably not as fast as socketed (though the practical difference may be zero), using ports is the more common way of interacting with Docker. Because of that, you may find that it is better documented, and therefore “easier” to implement in edge-cases.

Caddyfile (assuming app.yml)

forum.example.com {
    reverse_proxy app:80

Discourse config ( app.yml or web_only.yml ):

  1. Set Discourse to listen on non-standard Docker port
  - "8880:80"   # http
  1. Comment out or remove SSL templates
  # - "templates/web.ssl.template.yml"
  # - "templates/web.letsencrypt.ssl.template.yml"
  1. Get Caddy and Discourse on the same Docker network:
  - "8880:80"   # http
#   - "443:443" # https

# Use 'links' key to link containers together, aka use Docker --link flag.
  - link:
      name: caddy
      alias: caddy

# any extra arguments for Docker?
  - '--network caddy_default'

    external: true


  1. The ./launcher script expects the network to be running while the bootstrap or rebuild runs.

@francislavoie, @Whitestrake, et al,

I see that this got moved from Wiki to help, and I totally understand why. The part that’s working handles all the standard Discourse cases, so my main goal was to get that out there to help others. I then kinda got carried away with edge-cases, and it got confusing.

Would it make more sense to split this off into the standard/working socketed way in the wiki, then keep the port proxying stuff in a help request? If I’d intended it to be a help request, I would have provided a lot more details to help troubleshooting.

Anywho, just a thought. Feel free to move thins around, split the topic, delete this commentary, or whatever makes the most sense.

Yeah - I moved it away from a Wiki because it still had unanswered questions which I figured should be resolved before it being ratified as a wiki. I probably should’ve clarified that after doing it, sorry about that. I think once those questions are resolved, we can turn it into a wiki again (either possibly as a new thread of updating this once, but if the discussion happens here to answer the questions, I figure it would be better to open a new thread after the fact as the wiki).

1 Like

After spending more than just two seconds skimming the post, I think the issue here is that you’re using port 8880 here which is the post bound on the docker host, not the port internal to the docker network. I think it should work with app:80 here if Caddy is also in the same docker network.


I guess you could do: - ${DISCOURSE_PATH}/shared/app:/sock/app1 and so on? That’s area where TCP is at an advantage over unix sockets, i.e. there’s no overlap due to the filesystem.

1 Like

It’s working now. Thank you again. This is one of the most helpful forums I visit. It has a good vibe which really comes across, and I appreciate all of you.

I tried host:80 before, so I must have had one of the networking bits off while I had that right. Oh well, only another 10 hours of troubleshooting. :sweat_smile:

I’ll come back and rework the how-to after I get a little more testing done.


Gotcha – would love to see a wiki for this when it’s working!

Incidentally, this Discourse instance is fronted by Caddy as well, I had no troubles whatsoever… :thinking:

1 Like

It’s super crazy easy for 100% of single-forum (“standalone”, in Discourse parlance), and even the advanced standard multisite install (web_only.yml + data.yml).

It gets a little trickier when running Caddy in Docker along with multiple Discourse containers simultaneously. Those cases require reworking things because the Discourse-provided socketed template expects one directory/container.

The edge-cases are multisite Discourse requiring multiple front-end containers. This would be any scenario where each forum needs separate SMTP settings, separate plugins, or the ability to run some plugins which are not compatible with multisite.

I’m pretty sure what most Docker users would consider the normal way – ports – will work the same for all cases. However, the Docker networking isn’t set up in the default scripts Discourse provides.

1 Like

If I understand it correctly (doubtful!), the web.socketed.template.yml applies to the host side of Docker. The guest side should be stateless, so would it work to add /app1 inside the container?

My current thinking is docker-compose needs to have volume mapping which looks more like:

      - ${DISCOURSE_PATH}/shared:/sock

Then Caddyfile would look more like:

forum1.example.com {
    reverse_proxy unix//sock/nginx.http.sock1
forum2.example.com {
    reverse_proxy unix//sock/nginx.http.sock2

But to that to happen, the socketed template would have to be adapted to the sock1, sock2 magic.

Since the nginx.http.socket is at a fixed path, is there a way in Caddy to reverse-proxy to that with something more like an absolute path than the unix// locator?

Bottom line

At this point, socketed multi-container is more curiosity and a quest for parsimony than need. I’ve updated the OP with the working Port option, so folks should be able to use Caddy for 100% of use-cases with Proxy, and 99% with Socketed.

If there are improvements or confusing areas, I’d be happy to implement them. One thing that’s probably missing is links to the described installation methods.

To clarify, unix/ is the prefix that tells Caddy that what follows is a filesystem path to a unix socket, then /sock/nginx.http.sock1 is your absolute path. If you wanted a relative path then it would be unix/./nginx.http.sock1 or something like that.

1 Like

These are the magic words :tada:

    reverse_proxy unix//sock/app/nginx.http.sock # assuming app.yml

OP is updated to reflect that socketed and proxied can both work in every Docker+Discourse scenario I’ve thought about (well, aside from K8s, but that’s another story).

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.