DNS Tuning (recommended)

For federation, Matrix homeservers conduct an enormous amount of DNS requests, sometimes up to thousands of queries per minute. Normal DNS resolvers are simply not designed for this load, and running Continuwuity with them will likely result in various DNS and federation errors.

To solve this issue, it is strongly recommended to self-host a high-quality, external caching DNS resolver for Continuwuity. This guide will use Unbound as the recommended example, but the general principle applies to any resolver.

Overview

For generic deployments, install your resolver of choice and configure /etc/resolv.conf to point to it. The resolver should ideally reside on the same host as Continuwuity.

/etc/resolv.conf
nameserver 127.0.0.1

Avoid using systemd-resolved as it does not perform very well under high load, and we have identified its DNS caching to not be very effective.

For Docker users

Docker bridge networks uses a non-performant resolver to intercept and respond to container hostnames, and this should also be avoided. Instead, mount a custom /etc/resolv.conf file into the container, and hardcode a resolver address to bypass Docker's.

It is recommended to run a dedicated resolver container for Continuwuity, as to separate from the host's resolver setup. To do this, create a custom bridge network and IP range, and explicitly define an IP address for the resolver container.

Example Docker deployment with unbound
docker-compose.yml
networks:
  matrix_net:
    ipam:
      driver: default
      config:
        - subnet: "10.10.10.0/24"

services:
    homeserver:
        # ...
        volume:
            - ./continuwuity-resolv.conf:/etc/resolv.conf:ro

    unbound:
        # ...
        networks:
            matrix_net:
                ipv4_address: 10.10.10.20
continuwuity-resolv.conf
nameserver 10.10.10.20

For IPv4-only users

If you don't have IPv6 connectivity, changing ip_lookup_strategy to only resolve for IPv4 will reduce unnecessary AAAA queries.

continuwuity.toml
[global]
# 1 - Ipv4Only (Only query for A records, no AAAA/IPv6)
ip_lookup_strategy = 1

Unbound

Unbound is the recommended resolver to run with Continuwuity. For Docker users, the docker.io/madnuttah/unbound image (Github repo) can be used.

After installation, you can tune /etc/unbound/unbound.conf values according to your needs. While Continuwuity cannot recommend a "works-for-everyone" Unbound DNS setup guide, the official Unbound tuning guide and the Unbound Arch Linux wiki page may be of interest.

Some values that are commonly tuned include:

  • Increase rrset-cache-size and msg-cache-size to something much higher than the default 4M, such as 64M.

  • Increase discard-timeout to something like 4800 to wait longer for upstream resolvers, as recursion can take a long time to respond to some domains. Continuwuity default to dns_timeout = 10 seconds, so dropping requests early would lead to unnecessary retries and/or failures.

Using a forwarder (optional)

Unbound by default employs recursive resolution and contacts many servers around the world. If this is not performant enough, consider forwarding your queries to public resolvers to benefit from their CDNs and get faster responses.

However, most popular upstreams (such as Google DNS or Quad9) employ IP ratelimiting, so a generous cache is still needed to avoid making too many queries.

DNS-over-TLS forwarders may also be used should you need on-the-wire encryption, but TLS overhead causes some speed penalties.

If you want to use forwarders, configure it as follows:

unbound.conf
# Use cloudflare public resolvers as an example
forward-zone:
    name: "."
    forward-addr: 1.0.0.1@53
    forward-addr: 1.1.1.1@53
    # Also use IPv6 ones if you're dual-stack
    # forward-addr: 2606:4700:4700::1001@53
    # forward-addr: 2606:4700:4700::1111@53

# alternatively, use DNS-over-TLS for forwarders.
# forward-zone:
    # name: "."
    # forward-tls-upstream: yes
    # forward-addr: 1.0.0.1@853#cloudflare-dns.com
    # forward-addr: 1.1.1.1@853#cloudflare-dns.com
    # forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com
    # forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com

Other resolvers

dnsproxy

Dnsproxy and its sister product AdGuard Home are known to work with Continuwuity and has an official Docker image. They have support for DNS-over-HTTPS as well as DNS-over-QUIC, but not recursion.

To best utilise dnsproxy, you should enable proper caching with --cache and set --cache-size to something bigger, like 64000000.

dnsmasq

dnsmasq can possibly work with Continuwuity, though it only supports forwarding rather than recursion. Increase the cache-size to something like 30000 for better caching performance.

However, dnsmasq does not support TCP fallback which can be problematic when receiving large DNS responses such as from large SRV records. If you still want to use dnsmasq, make sure you disable dns_tcp_fallback in Continuwuity config.

Technitium

Technitium supports recursion as well as a myriad of forwarding protocols, allows saving cache to disk natively, and does work well with Continuwuity. Its default configurations however ratelimits single-IP requests by a lot, and hence must be changed. You may consult this community guide for more details on setting up a dedicated Technitium for Continuwuity.

Testing

As a rough stress test, you can run !admin query resolver flush-cache -a or !admin server clear-caches to trigger a netburst of DNS queries. If your resolver can handle these loads without problem, then it should be ready for regular Continuwuity activity.

To test connectivity against a specific server, use !admin debug ping <SERVER_NAME> and !admin debug resolve-true-destination <SERVER_NAME>.

Note that it is expected that not all servers will be resolved, as some of them may be temporarily offline, have broken DNS and/or discovery configuration, or have been decommissioned.

Further steps

  • (Recommended) Set dns_cache_entries = 0 inside Continuwuity and fully rely on the more performant external resolver.

  • Consider employing persistent cache to disk, so your resolver can still run without hassle after a restart. Unbound, via Cache DB module, can use Redis as a storage backend for this feature.

  • Consider enabling Serve Stale functionality to serve expired data beyond DNS TTLs. Since most Matrix homeservers have static IPs, this should help improve federation with them especially when upstream resolvers have timed out. For dnsproxy, this corresponds to its optimistic caching options.

  • If you still experience DNS performance issues, another step could be to disable DNSSEC (which is computationally expensive) at a cost of slightly decreased security. On Unbound this is done by commenting out trust-anchors config options and removing the validator module.

  • Some users have reported that setting query_over_tcp_only = true in Continuwuity has improved DNS reliability at a slight performance cost due to TCP overhead. Generally this is not needed if your resolver and homeserver is on the same machine.