# Run fancy-dns outside Kubernetes This Compose deployment follows `deploy/k8s/00-config.yaml` and `deploy/k8s/10-deployment.yaml`. It builds the image from this checkout instead of pulling `registry.k6n.net/fancydns`, avoiding a bootstrap dependency on the cluster registry DNS. > **Following the cluster's configuration.** This Compose setup is configured by > files and has no link to the cluster's `ClientGroup`, `DNSZone` and other > objects. To run a backup resolver that reads them (and keeps serving a saved > copy when the cluster is down), see [`../OUTSIDE-CLUSTER.md`](../OUTSIDE-CLUSTER.md). ## Prepare and start From the repository root: ```sh mkdir -p deploy/compose/{data,zones,rpz,tls} cp deploy/compose/.env.example deploy/compose/.env ``` Edit `deploy/compose/.env` (keep it private) and set a random API key as `FANCYDNS_API_KEY=`. Install the certificate and private key for `dns.k6n.net` as `deploy/compose/tls/fullchain.pem` and `deploy/compose/tls/privkey.pem`. Add the same zone files used by the cluster to `deploy/compose/zones/`. Keep private key and `.env` out of source control. The container runs as UID/GID 65532 with a read-only root filesystem, so make the persistent directory writable first, for example: ```sh sudo chown -R 65532:65532 deploy/compose/data docker compose --env-file deploy/compose/.env -f deploy/compose/compose.yaml build docker compose --env-file deploy/compose/.env -f deploy/compose/compose.yaml run --rm fancy-dns -config /etc/fancy-dns/config.yaml -check docker compose --env-file deploy/compose/.env -f deploy/compose/compose.yaml up -d ``` The DNS ports publish on all host interfaces: UDP/TCP 53, TCP 853, and TCP 443. The admin API is published only on `127.0.0.1:8080`; the embedded UI is disabled by default for backup-resolver use. To enable it, set `api.ui: true` in `config.yaml` and restart. Use an authenticated TLS reverse proxy for remote access. Check that nothing already owns host port 53 before starting. The sample mounts TLS files and enables DoT and DoH, so the files must exist before startup. ## How configuration is shared The repository is the source of truth, but there is currently **no automatic config distribution** between Kubernetes and the bare-metal Compose host. Kubernetes GitOps reconciles `deploy/k8s` from `main` (see `gitops/README.md`); the bare-metal host must get the updated checkout and have Compose restarted. Kubernetes gets its config from the ConfigMap in `deploy/k8s/00-config.yaml`; Compose reads the checked-in `deploy/compose/config.yaml` and files under `deploy/compose/`. Treat these as two deployment configs: commit reviewed changes to both when a shared resolver setting changes. The Compose config now includes the same `local.` forward zone to `10.10.0.1:53` as Kubernetes. The shared inputs to keep aligned are upstreams/forward zones, resolver policy, cache sizing/TTL, blocklist sources and actions, ACLs, local zone files, RPZ rules, and the DoT/DoH certificate for the same public DNS name. Deployment specific settings intentionally differ: listener container ports/storage paths must match each deployment, and Compose uses API-key auth with the UI disabled while Kubernetes uses OIDC. On bare metal the YAML, zone and RPZ files are bind-mounted; Kubernetes stores config, zones and RPZ in ConfigMaps and TLS in a Secret. Secrets should be provisioned separately on each system, not copied into Git. The Compose API key is in its private `.env` file. Do not share the `data/` directory or copy its bbolt files while either server is running. Cache snapshots and query logs are instance-local state. The managed zones written by the console also live under this directory; edits made on one instance do not appear on the other automatically. Make operator zone changes in the source of truth and deploy them to both, or designate one instance as the writer. Valkey clustering syncs cluster events/list leadership, not these files or bbolt state. The private forwarding target must remain reachable from both the Kubernetes pods and the bare-metal host. Verify that the bare-metal resolver has outbound access to its configured public upstreams before making it the cluster's registry resolver. For registry recovery, configure the Kubernetes nodes' OS resolver and the container runtime's registry DNS path to use this host's **static IP directly** as a DNS server (with another independent resolver as fallback). Do not rely on a hostname for the backup resolver, and do not make pulling this Compose image depend on `registry.k6n.net`: this Compose file builds the image locally from the repository checkout. A working route from the cluster nodes to the bare-metal host on UDP/TCP 53 is required; allow it through the host firewall. To update the local build after code changes, rebuild and recreate the container. The running server can hot-reload its YAML on SIGHUP, but Compose does not automatically send SIGHUP when bind-mounted files change.