Skip to content

Behind a reverse proxy ​

Give your community a permanent address such as chat.example.com, served through a reverse proxy you already run (Caddy, nginx, Traefik…). People join by typing the hostname. Relaying and peer discovery still use Moin's public defaults, so your server only runs the community itself.

Install the server first, or use Docker.

How it works ​

When someone adds chat.example.com, the app fetches https://chat.example.com/.well-known/moin/bootstrap. That small JSON document holds the server's ID, its UDP port, and which relay and discovery servers to use. The app then connects to the server over UDP, at the addresses your hostname resolves to.

So two paths have to reach the server:

PathCarriesGoes through
HTTPS on TCP 443the bootstrap documentyour proxy, which ends TLS and forwards plain HTTP
One UDP port, e.g. 7443the members' connections to the serverstraight to the server; proxies can't carry it

Before you start ​

  • A DNS record for your hostname, pointing at the public IP address that receives the UDP port. The app connects to every address the hostname resolves to, so don't publish an IPv6 (AAAA) record unless the UDP port works over IPv6 too.
  • No CDN in front of the hostname. A CDN swaps your address for its own, and those don't carry Moin's UDP traffic. On Cloudflare, set the record to "DNS only".
  • The UDP port forwarded to the machine running moin-server, through your firewall and router.

1. Write the configuration ​

toml
name = "Game Night"
rooms = "managed"

[access]
bootstrap_secret = "correct-horse-battery-staple"

[server]
origin = "https://chat.example.com"
http_bind = "127.0.0.1:8080"

[server.coordinator]
bind = "0.0.0.0:7443"
public_port = 7443
  • bootstrap_secret lets you claim the administrator role from the app; see the first administrator.
  • origin is the address people type, always https://. Include a port only if your proxy serves HTTPS on one other than 443.
  • http_bind is where the server listens for your proxy. Use 127.0.0.1 when the proxy is on the same machine. If it's on another machine or in a container, bind an address the proxy can reach, such as 0.0.0.0:8080, and keep that port closed to the internet.
  • [server.coordinator] is the UDP port. bind is the local address and port; public_port is the port people reach from outside. They differ when your router forwards a different external port.

See Configuration for every setting under [server].

2. Configure the proxy ​

Forward /.well-known/moin/bootstrap to the server. Nothing else is required.

With Caddy, which also gets the certificate for you:

text
chat.example.com {
	handle /.well-known/moin/bootstrap {
		reverse_proxy 127.0.0.1:8080
	}
}

With nginx, inside the server block that has TLS for your hostname:

nginx
location = /.well-known/moin/bootstrap {
    proxy_pass http://127.0.0.1:8080;
}

The hostname can serve a website on every other path.

3. Start and check ​

sh
moin-server --config moin-server.toml

From another machine:

sh
curl https://chat.example.com/.well-known/moin/bootstrap

You should get a JSON document. A 503 means the server isn't ready yet, and clears once startup finishes. A 502 or 404 comes from the proxy and means it isn't reaching the server.

curl doesn't test the UDP port. If the bootstrap works but the app can't connect, check the UDP port forwarding and the firewall first.

4. Claim the community and invite people ​

In Moin, choose Add a connection and type chat.example.com. Then carry on as in the coordinator-only guide, all from the app:

  1. Claim the community with the bootstrap secret, to become its administrator.
  2. Create rooms.
  3. Invite people with Share community. Links carry your hostname, and people can also just type it.
  4. Decide who gets in, if the community shouldn't be open to anyone who has the link.

See Members and access for the details.

Optional: host more yourself ​

The public relay and discovery servers keep working for your community, so this setup is complete as it is. You can add Moin's own relay and discovery service behind the same proxy:

  • Discovery ([server.pkarr]): also forward /pkarr/ to the server. It's ordinary HTTP.
  • Relay ([server.relay]): also forward /relay, /ping, /generate_204 and /healthz. The proxy must allow WebSocket upgrades and connections that stay open for hours.
  • Address probing ([server.relay.qad]) uses its own UDP port with TLS, so it can't go through the proxy. It needs a certificate configured on the server itself; see Certificates.

To stop depending on the public servers entirely, see Self-contained.