> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dnzwithazee.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reverse proxies only

> Run DNZ as a manager for DNZ Proxy, Traefik, Caddy and Nginx without its DNS server: one container, records on Cloudflare, certificates over HTTP-01 or DNS-01.

DNZ manages reverse proxies as well as DNS. When your DNS lives somewhere else (Cloudflare, your
registrar, your own servers) and you only want the proxies, start DNZ with `--proxy-only`: no DNS
server, and none of its pages, commands or tools.

```bash theme={null}
dnz --proxy-only
```

The same as `DNZ_PROXY_ONLY=true`, or at the top of the config file:

```toml theme={null}
proxy_only = true
```

It is the same `dnz` and the same database. Starting the same data folder without `--proxy-only`
later turns DNS on, and everything made meanwhile is still there.

## In one container

```yaml docker-compose.proxy-only.yml theme={null}
services:
  dnz:
    image: terrificci/dnz:latest
    command: ["--proxy-only"]
    restart: unless-stopped
    ports:
      - "80:80"      # visitors: the DNZ Proxy node in this container
      - "443:443"
      # - "443:443/udp"  # with HTTP/3 on the entry point, and UDP 443 open on the host's firewall
      - "8080:8080"  # the UI and API
    volumes:
      - dnz-data:/data
    environment:
      TZ: UTC
      # The address visitors reach this host at, for the records DNZ writes on Cloudflare:
      # DNZ_PROXY_PUBLIC_IPV4: 203.0.113.10

volumes:
  dnz-data:
```

```bash theme={null}
docker compose -f docker-compose.proxy-only.yml up -d
```

At its first start (no proxy yet) DNZ turns proxying on, makes a DNZ Proxy called **Default** and runs
a node of it in the same container, which serves your routes on ports 80 and 443. Open
`http://<host>:8080`, create the admin (two steps: the admin and the first division), connect Cloudflare
if your domains are there ([optional](https://docs.dnzwithazee.com/get-started/first-run.md#connect-cloudflare-optional): **Skip** goes
straight in) and add a route under **Proxy → Routes**. More nodes on other servers join the same proxy as always
([DNZ Proxy](https://docs.dnzwithazee.com/proxy/dnz-proxy.md)).

To use HTTP/3 for visitors, turn it on at the entry point, uncomment the `443:443/udp` line above and
open UDP 443 on the host's firewall.

On a Linux server without Docker, the installer sets up the service the same way:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/terrific-ci/dnz/main/install.sh | sh -s -- --proxy-only
```

## What stays

* **Proxies:** every kind, nodes, routes (HTTP, TCP, UDP), groups, batch edit, route history,
  [configuration as code](https://docs.dnzwithazee.com/proxy/config-as-code.md), upstreams (health checks,
  [servers from a DNS name](https://docs.dnzwithazee.com/proxy/upstream-discovery.md)), middlewares, [rules](https://docs.dnzwithazee.com/proxy/rules.md), the
  [WAF](https://docs.dnzwithazee.com/proxy/waf.md), the firewall, Under attack, rate limits, the waiting room,
  [sign-in](https://docs.dnzwithazee.com/proxy/sign-in.md), caching, analytics, request logs, visitors and
  [status pages](https://docs.dnzwithazee.com/proxy/status-pages.md).
* **Certificates:** ACME (Let's Encrypt, ZeroSSL, any ACME CA), uploads, the
  [private CA](https://docs.dnzwithazee.com/proxy/private-ca.md), DNZ's own certificate (Settings → General → Certificate).
* **Operations:** Uptime and the public status page, proxy alerts, notifications, the
  [cluster](https://docs.dnzwithazee.com/deploy/cluster.md), backups, the audit log, diagnostics, Prometheus.
* **Access:** users, roles, divisions, API tokens, two-factor, sign-in with Google, the CLI, the CLI in
  the browser, MCP.
* **Cloudflare:** connections per division and the Cloudflare zones (DNS → Zones): records, traffic,
  SSL/TLS, DNSSEC, cache purge, tunnels.

## What is off

* Port 53, and DNS over TLS, HTTPS (`/dns-query`), QUIC and HTTP/3. HTTP/3 for visitors is DNZ
  Proxy's and stays.
* Local zones and records, changes and approvals, views, forwarding, filtering, threats, DHCP & IPAM,
  typo help, and [decoy names](https://docs.dnzwithazee.com/proxy/decoys.md), which are turned on per local zone.
* The query log, Live Capture, Query Explain.
* DNS replication (primary and secondary servers); proxy-only servers have [standbys](#a-standby) instead.
* DNS alerts (upstreams, NXDOMAIN spikes, domain expiry, the daily summary) and DNS metrics.

Their pages are gone from the menu (their addresses lead to the Overview), their API endpoints answer
`404` with `DNS is off on this server (--proxy-only)`, the CLI prints the same, and MCP lists none of
their tools. The Overview opens on **Proxy**. Local zones a server had before it was started with
`--proxy-only` are kept, but not served or shown.

## What works differently

* **[Name checks](https://docs.dnzwithazee.com/dashboard/name-checks.md):** origins that can be reached without the proxy and exposed
  files are checked; CNAMEs pointing at targets that are gone or can be claimed are not, since DNZ
  holds no zones here.
* **[Private CA's ACME server](https://docs.dnzwithazee.com/proxy/private-ca.md#acme):** `http-01` works as always. For `dns-01` DNZ
  answers nothing itself: the client publishes the TXT record in your DNS, and DNZ reads it through
  the server's resolvers.
* **[Log push](https://docs.dnzwithazee.com/integrations/logpush.md):** only DNZ Proxy's request log can be pushed.

## DNS records for your routes

DNZ answers no DNS here, so a route's name has to point at the proxy where its DNS is:

* **With a Cloudflare connection** (Settings → Integrations), *Keep its DNS records on Cloudflare* in
  the route dialog writes them: one A/AAAA record per address of the proxy (for a DNZ Proxy, its
  healthy nodes that aren't drained), in the name's Cloudflare zone, DNS only, with the comment
  *Synced from DNZ*. When a node comes or goes the records follow within about a minute and a half (a
  node that flaps doesn't rewrite them), and they go with the route.
* The route dialog's hostname field offers the connected accounts' zones as you type and tags a name
  in one of them *new* (records will be added there), or with the record types DNZ already keeps for
  it; a name in none of them is *no zone* and can still be routed. Without a connection every name is
  *no zone*. `dnz proxy hosts <proxy> [name]` lists the same.
* Records made on Cloudflare by hand for the same name are never changed: the route says so, and the
  name keeps pointing where it did.
* **Proxied by Cloudflare**, under the same option, writes them through Cloudflare's proxy instead:
  visitors reach Cloudflare, which forwards them to the proxy. Trust Cloudflare's addresses on the
  entry point (its forwarded-from list), or the firewall, rate limits and logs see Cloudflare instead of
  the visitor; DNS-01 proves names behind it most reliably. Switch it on an existing route in its
  dialog, or with `dnz proxy routes cloudflare <proxy> <host> --proxied on|off`.
* **Without one** (or for a name in no connected zone), the route dialog and
  `dnz proxy routes add` show the addresses to point the name at yourself.

[Read-only proxies](https://docs.dnzwithazee.com/proxy/read-only.md) (a Caddy or Traefik DNZ doesn't configure) write their names'
records on Cloudflare the same way.

## Certificates

Automatic certificates never use DNZ's own zones here. **Prove names with** (Proxy → Certificates)
picks the order:

| Choice | How a name is proven |
| - | - |
| **HTTP-01 first** (default) | Let's Encrypt asks the nodes for a token on port 80 once the name points at them; names that don't yet go through Cloudflare or a DNS credential (DNS-01) |
| **DNS-01 first** | A TXT record through the zone's Cloudflare account or a DNS credential (a Cloudflare token, RFC 2136 or a webhook); the nodes answer for names neither covers |

```bash theme={null}
dnz proxy certs prove http   # or dns
```

Wildcards (`*.example.com`) always take DNS-01; without a way for it, the route dialog says how to add
one. See [Certificates](https://docs.dnzwithazee.com/proxy/certificates.md).

## Upstreams from a DNS name

DNZ looks an upstream's name up through the server's own resolvers (`/etc/resolv.conf`), with their
TTLs. Names each proxy looks up itself (Docker Swarm's `tasks.<service>`) work as always.

## A standby

A second proxy-only DNZ, on another server with its own database, can hold a copy of this one and
take over when it is gone.

**1. Make its join code**

On this server: **High availability → Standby → Add standby**, or:

```bash theme={null}
dnz standby add dnz-b
```

Give this server's address as the standby reaches it. DNZ shows the join code once.

**2. Follow from the standby**

On the second server (a fresh DNZ started with `--proxy-only`): **High availability → Standby →
Follow a primary** and paste the code, or:

```bash theme={null}
dnz standby follow 'dnz-standby:…'
```

The standby then copies everything every 30 seconds: proxies, routes, upstreams, middlewares, nodes
(with their tokens), certificates with their keys, the private CA, ACME accounts, DNS credentials,
Cloudflare connections, notifications, uptime monitors, incidents and their history, users and roles.
Each server keeps its own sessions, audit log and diagnostics. An unchanged copy costs one small
request.

While it follows, the standby leaves the install's jobs to its primary, so nothing is done twice:
alerts and reports, uptime and name checks, renewing and signing certificates, writing records on
Cloudflare, updating nodes and scheduled backups. It starts doing them once it is promoted or takes
over.

> [!WARNING]
>
> The join code carries a secret: anyone with it can copy the server, keys included. When the two
> servers talk over the internet, give the standby an `https://` address of the primary.

While it follows, the standby is read-only (it refuses changes and says so) and tells DNZ Proxy nodes
that call it where the primary is. Its own node serves no traffic by default. **Serve traffic while
standby** (on its Standby page, or `dnz standby serve on`) makes that node one more node of the
primary's proxy: both hosts take visitors, and nothing stops when the standby takes over. DNS, or the
records on Cloudflare, then answer with the standby's address too.

### Swapping the two while the primary runs

Moving the primary to new hardware, or upgrading it: take over from the standby (**Take over**, or
`dnz standby take-over --url <this server's address>`). The primary stops taking changes, sends its
last copy and follows the standby from then on; the standby becomes the primary, with the old primary
and the other standbys as its standbys. Standbys and DNZ Proxy nodes that still call the old primary
are told where the new one is and go there by themselves. If the standby can't finish, the old primary
is the primary again.

### When the primary is gone

Promote the standby (**Promote**, or `dnz standby promote`). It takes changes from then on. The DNZ
Proxy nodes know where it is: the primary lists its standbys to them, and a node that can't reach its
DNZ three times in a row tries the next one, so they move by themselves and keep serving their last
configuration meanwhile.

If the old primary comes back, stop it: two primaries would each think they manage the nodes. To make
it a standby of the new one, start it fresh (an empty data folder) and follow the new primary.

Both servers must run the same DNZ version, or the standby a newer one: a standby refuses a copy from
a newer primary until it is updated.

## A cluster

Every server of a [cluster](https://docs.dnzwithazee.com/deploy/cluster.md) runs in the same mode: a server started with
`--proxy-only` next to full ones (or the other way) refuses to start and says why.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.