Self-contained
Run everything your community needs on your own server: the community itself, a relay, peer discovery, and address probing. With the public defaults turned off, your community works without any server run by someone else. You can also keep the public servers as a fallback alongside your own.
Install the server first, or use Docker.
What the server provides
| Service | What it does | Public port |
|---|---|---|
| Bootstrap | Tells the app how to reach your server when someone types your hostname | TCP 443 |
| Relay | Passes traffic between devices that can't reach each other directly, and helps them connect directly | TCP 443 |
| Discovery | Lets members' devices find each other's current addresses | TCP 443 |
| The community | Members' connections to the server | UDP 443 |
| Address probing (QAD) | Tells devices their public address, which helps direct connections | UDP 7842 |
All three TCP services share one HTTPS listener. The ports can be changed; these are the defaults the guide uses.
Before you start
- A public IP address on the machine, or a router that forwards these ports to it: TCP 443, UDP 443, UDP 7842.
- A DNS record for your hostname pointing at that address, with no CDN in front of it. Publish an IPv6 (AAAA) record only if all three ports work over IPv6 too.
- TCP 443 reaching Moin directly, if you want automatic certificates. Automatic certificates prove control of the hostname on port 443 itself, so a proxy that ends TLS there gets in the way. A proxy that passes TCP through untouched is fine. Otherwise, use your own certificate files.
- Permission to bind port 443. Run the server with the
CAP_NET_BIND_SERVICEcapability (see Running the server), or bind high ports and forward 443 to them.
1. Write the configuration
toml
name = "Game Night"
rooms = "managed"
[access]
bootstrap_secret = "correct-horse-battery-staple"
[server]
origin = "https://chat.example.com"
https_bind = "0.0.0.0:443"
[server.coordinator]
bind = "0.0.0.0:443"
public_port = 443
[server.certificates.acme]
contact = ["mailto:you@example.com"]
directory = "https://acme-v02.api.letsencrypt.org/directory"
[server.relay]
max_connections = 1024
bytes_per_second = 1048576
burst_bytes = 2097152
[server.relay.qad]
bind = "0.0.0.0:7842"
public_port = 7842
[server.pkarr]
capacity = 10000
retention_seconds = 604800
requests_per_second = 100
max_in_flight = 16
[server.relays]
include_defaults = false
[server.discovery]
include_defaults = falseWhat each part does:
bootstrap_secretlets you claim the administrator role from the app; see the first administrator.[server]sets the address people type and the HTTPS listener.https_bindand the coordinator'sbindcan both use port 443, because one is TCP and the other UDP.[server.coordinator]is the UDP port for members' connections.public_portis the port reachable from outside, if your router maps it to a different one.[server.certificates.acme]gets and renews a certificate from Let's Encrypt on its own. See Certificates for testing first, or for using your own certificate files.[server.relay]turns on the relay. The numbers cap connections and each connection's bandwidth, in bytes per second; the values above suit a small community. See Relay.[server.relay.qad]turns on address probing on its own UDP port. Leave it out if you'd rather not expose it; see Exposure.[server.pkarr]turns on discovery and sets how many device records it keeps, for how long, and how fast it answers. See Discovery.include_defaults = falsein[server.relays]and[server.discovery]removes the public servers, so devices only use yours.
The relay and discovery service are advertised to members automatically. You don't list them anywhere.
2. Start and check
sh
moin-server --config moin-server.tomlThe startup log includes a stable coordinator started line listing which of relay, qad and pkarr are on. The first start may take a little longer while the certificate is issued.
From another machine:
sh
curl https://chat.example.com/.well-known/moin/bootstrapYou should get a JSON document that lists your hostname as the relay and https://chat.example.com/pkarr for discovery. A 503 means the server isn't ready yet: it answers once it has connected to its own relay. A certificate error means the certificate hasn't been issued yet, or comes from the staging directory. curl can't test the UDP ports; if the bootstrap works but the app can't connect, check UDP forwarding and the firewall first.
3. 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:
- Claim the community with the bootstrap secret, to become its administrator.
- Create rooms.
- Invite people with Share community. Links carry your hostname, and people can also just type it.
- Decide who gets in, if the community shouldn't be open to anyone who has the link.
See Members and access for the details.
Certificates
Testing with Let's Encrypt staging
Let's Encrypt limits how many certificates a hostname can get each week. While you're still working out DNS and ports, use its staging directory instead:
toml
directory = "https://acme-staging-v02.api.letsencrypt.org/directory"Staging certificates aren't trusted, so the app won't connect. Switch to the production directory before inviting anyone.
The certificate and the ACME account key are kept in acme inside the data directory. The account key is private, so keep that directory readable only by the server's user.
Your own certificate files
If you already manage certificates, or port 443 can't reach Moin directly, use files instead of [server.certificates.acme]:
toml
[server.certificates.manual]
cert = "/etc/moin/tls/fullchain.pem"
key = "/etc/moin/tls/privkey.pem"cert is the full chain in PEM format, key its private key. Relative paths are relative to the configuration file. The server checks the files every minute and switches to renewed ones without restarting. Replace both files together; if they don't match, the server keeps using the previous pair and tries again at the next check.
Keeping the public servers as a fallback
To run your own relay and discovery but still let devices use the public ones, remove [server.relays] and [server.discovery] from the configuration, or set include_defaults = true. Devices then use your servers and the public ones.
You can also add other relay or discovery servers you trust:
toml
[server.relays]
include_defaults = false
servers = [{ url = "https://relay.example.net", quic_port = 7842 }]
[server.discovery]
include_defaults = false
servers = [{ url = "https://discovery.example.net/pkarr" }]quic_port is that relay's address probing port; leave it out if it has none. See Relays and discovery.
Exposure
Everything on TCP 443 has limits built in: total connections, relay connections, bandwidth per connection, and discovery requests. Address probing on UDP 7842 has no connection limit of its own. If you expect hostile traffic, limit connections to that port at your firewall, or remove [server.relay.qad]. The relay keeps working without it.
The limits are listed under Limits.
Back up
Besides the configuration file, back up the data directory. It holds the server's identity, its memberships and rooms, the discovery records (pkarr.redb) and the certificate cache. See The data directory.
