Network Configuration¶
An App Server is reachable over HTTPS at one or more FQDNs. This page is the reference for two things: how that name is built, and what makes a URL public.
For the framework — the path from a visitor to your process, the URLs an App Server is given, and what every control on the Network tab does — start with How Muppy Exposes Your Application.
How an FQDN is built¶
FQDN = <hostname> . <domain>
<hostname>— the DNS label, produced by an FQDN Generator.<domain>— the DNS zone, from the App Server's Traefik server, or a forced DNS domain.
An App Server exposes two kinds of URL:
| URL | Source | FQDN Generator |
|---|---|---|
| Direct URL | the App Server's own reverse-proxy entry | Direct URL FQDN Generator |
| Public URLs | optional Traefik Application Definitions | the definition's own generator |
The generators used for public URLs, and why a public name drops the host and the company, are in Tenants, Domains and Host Names. The rest of this section covers the Direct URL.
The wildcard problem¶
A single-level wildcard certificate covers exactly one label under the domain:
*.example.comcoversmyapp.example.com✅*.example.comdoes not covermyapp.host1.example.com❌ (that needs*.host1.example.com)
So the hostname must be a single flat label — no extra dots.
Choosing the Direct URL generator¶
The Direct URL FQDN Generator field, on the App Server and on the App Definition,
controls the Direct URL's hostname. Each generator is named after the shape it produces, and
the default is [appserver-host] {ds_name}-{host_name}:
<server-name>-<host> -> myapp-dev-cyril-msa21.example.com
The host segment keeps URLs unique when several App Servers share a domain on different
hosts. It is folded in as a dash, so the whole thing stays one DNS label and a
*.example.com wildcard covers it.
The form you select is what decides whether the host is in the name. Selecting
[appserver-plain] {ds_name} leaves it out, which is safe only when the server name is
already unique within the domain — and the proxy configuration enforces that, refusing a name
another server already serves. The full list of forms, and which one fits which situation, is
in Tenants, Domains and Host Names.
To apply a change: set the generator, make sure the Traefik server's DNS domain has a matching wildcard certificate, then press (re)Configure 'Traefik Applications'.
Generators are inline — they hold editable code, so a naming scheme none of them covers means duplicating the closest one, editing its copy, and selecting it here.
Standard App Definitions are already set
The standard App Definitions (Odoo 18/19, Muppy, Sunray, Ubuntu) default to the flat Direct URL FQDN Generator, so new App Servers are wildcard-ready out of the box.
An empty generator means dotted names
With no Direct URL FQDN Generator, the App Server names itself
myapp-dev-cyril.msa21.example.com — a dotted sub-label that *.example.com does
not cover. Select the generator and reconfigure to move to the flat form.
One form does not belong here
[appserver-name_complement] {name_complement} names a server after its commercial
name alone. It is meant for a Traefik Application Definition set by hand: selected as a
Direct URL generator on a server with no Name complement, it leaves that server with
no Direct URL at all.
Public URLs behind Cloudflare¶
The Direct URL above resolves to the host's own IP. A public URL is served through
Cloudflare instead: anti-DDoS, WAF and cache in front, the origin IP hidden. It is a second
Traefik application on the App Server, generated from a Traefik Application Definition
whose forced DNS domain is a Cloudflare-backed domain: the zone itself, when this Muppy holds
the Cloudflare credential, or the tenant's MBD Client domain — one per tenant, such as
<client>.<zone> — when it does not (see
Muppy Built-in DNS). Which of the two a zone offers is
decided in Preparing a Cloudflare Zone for Public URLs.
The settings that make a URL public¶
| Setting | Value | Why |
|---|---|---|
| Forced DNS Domain | the Cloudflare zone, or the tenant's MBD Client domain with Proxied by Default (via MBD Server) ticked | the record the Traefik script creates is asked proxied, TTL Automatic |
| FQDN Generator | one that produces a public name | each generator is named after the shape it produces: [appserver-plain] {ds_name} on a tenant's own sub-zone, where the domain already names the company; [appserver-company-host] {ds_name}-{company}-{host_name} on a zone shared between companies. See Tenants, Domains and Host Names |
| DNS Records Strategy | Create | the record follows the application, whatever the Traefik server's own setting |
| Public Exposure | on | the URL becomes the App Server's default public URL, first in APP_PRIMARY_URL and APP_LOADBALANCER_URL |
| Use Let's Encrypt | off while the zone runs in Cloudflare's Full (not strict) mode | the origin serves Traefik's default certificate, which Cloudflare accepts; the LEGO credentials of the Traefik server belong to its own domain, not to the forced one |
| Enable ipWhiteList + Allowed CIDRs | optional | restricts who may visit this one application: the addresses of the people allowed in, an office or the muppy.io workers. Never the Cloudflare ranges |
| Client Address | The address forwarded by a trusted proxy, when the whitelist is on | makes the whitelist compare the visitor rather than the Cloudflare edge. Requires the matching setting on the Traefik Server, below |
A tenant's own name in front of the public URL, erp.acme.com for erp-prod.acme.<zone>, is
a Custom Domain on the Traefik Application, not a setting of the definition. Filling the
field routes the name; the Declare button next to it gets the certificate. See
Custom Domains.
Protecting the origin¶
The origin is protected at the host firewall, not in Traefik. Attach the Traefik host to
the CloudflareIPsV4 and CloudflareIPsV6 Dynamic CIDR Ranges: its UFW rule on port 443 then
admits Cloudflare's edge and the host's other ranges only, and a visitor who resolves the
origin IP and sends the public Host header directly is dropped. Sync the two ranges once
before attaching them (their CIDRs arrive with the range's Sync button, never at module
update), and update the host firewall after any later sync.
Restricting who may visit¶
Enable ipWhiteList restricts the visitors of one application to a CIDR range — an office, the muppy.io workers. Those are the addresses of the people allowed in; the Cloudflare ranges never belong there.
Behind Cloudflare, a visitor reaches Traefik through the edge, so filtering them takes two settings, in this order:
- On the Traefik Server, tab Main configuration, attach
CloudflareIPsV4andCloudflareIPsV6to Trusted forwarding proxies. Press (Re)load next to the main config template — the server keeps its own copy of the template body, and the rendered configuration comes from that copy — then Reconfigure the server. This is static configuration: Traefik restarts. From then on only connections from Cloudflare keep theirX-Forwarded-Forheader; every other connection has it dropped and rebuilt. - On the App Definition, set Client Address to The address forwarded by a trusted
proxy. The middleware then compares the last
X-Forwarded-Forentry, the one Cloudflare appended, which is the visitor as the edge saw them.
The order matters. Asking for the forwarded address before the server trusts the proxy leaves the middleware with no address at all, and the application answers 403 to everybody, including you. The Traefik Application form shows a red banner while that is the case, and the App Definition shows one when the selected template cannot render the setting.
Trusting a proxy is server-wide: every application on that Traefik server then receives the
X-Forwarded-* headers of the edge, which is what Odoo's proxy_mode wants. It also means
whoever can push traffic through that proxy chooses the address Traefik attributes to them, so
list nothing there but a proxy you own or pay for.
The first minutes, and hosts that cannot do this¶
The first minutes of a new public URL
Cloudflare issues an edge certificate per proxied hostname (Total TLS) after the record appears: about a minute in our measurements, a few minutes at most. Until then the URL answers a TLS error. Removing the public application deletes the record and, with it, the certificate: exposing the App Server again costs the same wait, about three minutes in our measurements. The App Server's system health check targets the public URL and is created without alerts, so this shows as a transient red state, not as a notification.
Hosts without a public IP
A public URL is an A record carrying the Traefik host's public IP, whichever path writes it. A host reached only through a private or Tailscale address needs a Cloudflare Tunnel instead, declared on a Cloudflare-native domain; an MBD Client domain cannot express a tunnel.