Skip to content

DNS & TLS

Two pieces make https://anything.coding9.test work in your browser with a green padlock and no host-file editing: a DNS layer that points the whole wildcard at 127.0.0.1, and a TLS layer that issues a locally-trusted wildcard certificate.

MultiDocker resolves *.<base-domain> without touching /etc/hosts:

  1. The edge stack runs dnsmasq (bound to 127.0.0.1:53) configured to answer every *.<base-domain> query with 127.0.0.1.

  2. A macOS resolver file at /etc/resolver/<base-domain> forwards all lookups for that domain to 127.0.0.1:

    /etc/resolver/coding9.test
    nameserver 127.0.0.1

scripts/setup-dns.sh writes one resolver file per configured base domain. It’s idempotent (marked with a # managed by multi-docker comment) and cleans up stale files. Writing to /etc/resolver/ needs root, which is why mdocker setup asks for sudo once.

HTTPS is provided by a wildcard certificate issued by mkcert, which installs a local certificate authority into your system trust store.

scripts/setup-tls.sh:

  • Registers the mkcert CA in the System Keychain (so browsers trust it).
  • Generates a wildcard certificate *.<base-domain>, plus a SAN for every additional base domain and every project-specific wildcard host (for example *.tenant.acme.coding9.test).

The certificate and CA live under ~/.config/multi-docker/certs/:

~/.config/multi-docker/certs/
├── wildcard.pem
├── wildcard-key.pem
└── ca/{rootCA.pem, rootCA-key.pem}

Traefik mounts wildcard.pem/wildcard-key.pem read-only and terminates TLS with them.

An X.509 wildcard matches a single label only: *.coding9.test covers app.coding9.test but not a.b.coding9.test. Any deeper wildcard a project needs must be added as its own SAN — which the render engine and host add handle for you.

Why browsers may ask for “Local Network” permission

Section titled “Why browsers may ask for “Local Network” permission”

Because domains resolve to container IPs on your machine, some browsers on recent macOS versions ask for the Local Network permission before they’ll connect. Grant it once and the padlock works normally.

Running mdocker config set BASE_DOMAIN <new> cascades automatically: the certificate, the resolver files, every project override and the edge stack are all regenerated. Restart your browser afterwards so it picks up the (unchanged) CA and the new hostnames. See Changing the Base Domain.