milestone 13: restructure docs to diataxis, tutorial, every command executed
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
# Install nxdns with Docker
|
||||
|
||||
Builds the nxdns image and runs it with Docker Compose. At the end a container
|
||||
answers DNS on port 53 and keeps its data in a named volume.
|
||||
|
||||
For what each configuration field means, see
|
||||
[the configuration reference](../reference/configuration.md).
|
||||
|
||||
> Verification: every command on this page was run on the machine that wrote
|
||||
> it, with three exceptions marked below — the `chown` to uid 65532 needs root,
|
||||
> the arm64 image was built but not run, and pushing to a registry needs
|
||||
> credentials. One command was run in altered form: host port 8080 was occupied
|
||||
> here, so the run and the two verification commands in step 3 were executed
|
||||
> with the host side of the port mappings moved to 25353 and 28088 rather than
|
||||
> the 53 and 8080 printed below. The container side was unchanged. See the note
|
||||
> in step 3.
|
||||
|
||||
## 1. Build the image
|
||||
|
||||
The Dockerfile does not compile anything. It assembles a filesystem around a
|
||||
binary you build first, so build the admin interface and the binaries from the
|
||||
repository root:
|
||||
|
||||
```sh
|
||||
(cd web && npm ci && npm run build)
|
||||
zig build cross -Dweb-dist=web/dist -Doptimize=ReleaseSafe
|
||||
docker build -t nxdns -f deploy/docker/Dockerfile .
|
||||
```
|
||||
|
||||
Build `web/dist` every time, before the binary. A stale bundle is embedded
|
||||
silently and ships an admin interface that does not match its API.
|
||||
|
||||
The context has to be the repository root, because the Dockerfile copies
|
||||
`zig-out/cross`. The result is a `scratch` image holding the binary, a CA
|
||||
bundle and two empty directories — 28.2 MB here.
|
||||
|
||||
Compose runs the same build with the right context:
|
||||
|
||||
```sh
|
||||
docker compose -f deploy/docker/compose.yaml build
|
||||
```
|
||||
|
||||
Every block on this page runs from the repository root, and none of them change
|
||||
directory, so they can be pasted in order. `-f` is what makes that work:
|
||||
Compose resolves the relative paths inside `compose.yaml` — the build context,
|
||||
the `etc-nxdns` bind mount — against the directory holding the file, not
|
||||
against your shell, and it takes the project name `docker` from that directory
|
||||
either way, which is why the container is `docker-nxdns-1`.
|
||||
|
||||
## 2. Write the seed configuration
|
||||
|
||||
Compose bind-mounts `deploy/docker/etc-nxdns` read-only at `/etc/nxdns`. Create
|
||||
it and put the seed file in it:
|
||||
|
||||
```sh
|
||||
mkdir -p deploy/docker/etc-nxdns
|
||||
$EDITOR deploy/docker/etc-nxdns/config.zon
|
||||
```
|
||||
|
||||
The smallest file that starts is one group named `default` and one enabled
|
||||
upstream:
|
||||
|
||||
```zon
|
||||
.{
|
||||
.groups = .{ .{ .name = "default" } },
|
||||
.upstreams = .{ .{ .url = "https://cloudflare-dns.com/dns-query" } },
|
||||
.web = .{ .password = "choose-a-real-password" },
|
||||
}
|
||||
```
|
||||
|
||||
Without that file the container exits with code 2 on a fresh volume: an empty
|
||||
database has nothing to forward to. The log is
|
||||
`no configuration file at '/etc/nxdns/config.zon'; using the database as it is`
|
||||
followed by `nxdns run failed: NoUsableUpstreams`.
|
||||
|
||||
A file that is present but rejected is a different failure and a different exit
|
||||
code. No `default` group, no enabled upstream, a syntax error — `run` prints the
|
||||
diagnostic and exits **1**, for example `nxdns run failed: MissingDefaultGroup`.
|
||||
Both were run here against this image: a seed file whose only group was named
|
||||
`other` exited 1, and an empty `/etc/nxdns` exited 2 with `NoUsableUpstreams`.
|
||||
Under `restart: unless-stopped` either one is a restart loop, so read the exit
|
||||
code from `docker inspect` to tell them apart; see
|
||||
[Troubleshoot nxdns](troubleshoot.md).
|
||||
|
||||
The container runs as uid 65532, and the mount is read-only, so the container
|
||||
cannot repair permissions itself. Mode 0644 works and was used here. If the
|
||||
file carries a secret — `web.password`, or a `web.password_hash` from a
|
||||
restored export — give it to that uid instead:
|
||||
|
||||
```sh
|
||||
chown 65532:65532 deploy/docker/etc-nxdns/config.zon
|
||||
chmod 0600 deploy/docker/etc-nxdns/config.zon
|
||||
```
|
||||
|
||||
> Not verified on this host: `chown` to a uid you do not own needs root. What
|
||||
> was verified is the failure it prevents — a seed file at 0600 owned by
|
||||
> another uid makes the container log `nxdns run failed: AccessDenied` and
|
||||
> restart in a loop. See [Troubleshoot nxdns](troubleshoot.md).
|
||||
|
||||
## 3. Run it
|
||||
|
||||
```sh
|
||||
docker compose -f deploy/docker/compose.yaml up -d
|
||||
docker compose -f deploy/docker/compose.yaml logs -f
|
||||
```
|
||||
|
||||
A healthy first start logs the seeding and the bound sockets:
|
||||
|
||||
```
|
||||
info(config_bootstrap): seeded the database from '/etc/nxdns/config.zon'
|
||||
info(nxdns): nxdns 0.1.0-dev serving on udp [::]:53 tcp [::]:53 tcp 0.0.0.0:53; 1 upstream(s); blocklist generation 1
|
||||
info(web_server): web interface listening on 0.0.0.0:8080
|
||||
```
|
||||
|
||||
Confirm it answers and that the admin interface is up:
|
||||
|
||||
```sh
|
||||
dig @127.0.0.1 example.com A +short
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
|
||||
```
|
||||
|
||||
> On the machine that wrote this page, host port 8080 was already taken by an
|
||||
> unrelated process, so the container was verified with the host side of the
|
||||
> port mappings moved to 25353 and 28088 — everything inside the container was
|
||||
> unchanged, and the log still reads `serving on udp [::]:53`. Against those
|
||||
> ports `dig` returned the A records for `example.com` and `curl` returned 200.
|
||||
> `docker compose up -d` fails with
|
||||
> `failed to bind host port 0.0.0.0:8080/tcp: address already in use` when a
|
||||
> host port is occupied; free the port or edit the `ports:` list.
|
||||
|
||||
The compose file publishes 53/udp, 53/tcp and 8080, keeps `/var/lib/nxdns` in
|
||||
the named volume `nxdns-data`, and sets the per-namespace sysctl
|
||||
`net.ipv4.ip_unprivileged_port_start=0` so uid 65532 can bind port 53 without
|
||||
any capability. Uncomment the 443 and 853 mappings when you enable the DoH or
|
||||
DoT listener; see [Enable DoH and DoT](enable-doh-and-dot.md).
|
||||
|
||||
## 4. Do not point the host at the container
|
||||
|
||||
The container resolves its own upstream DoH and DoT hostnames through the
|
||||
host's DNS configuration. If you set the host's `/etc/resolv.conf` to the nxdns
|
||||
container, the container's startup lookups depend on the service that is trying
|
||||
to start. Point LAN clients at nxdns; leave the container's host on its own
|
||||
resolver.
|
||||
|
||||
## Build for a Raspberry Pi 5
|
||||
|
||||
The Dockerfile maps buildx's `TARGETARCH` onto the cross-target directory, so
|
||||
the aarch64 image comes from the same `zig-out/cross` tree with no second
|
||||
compile:
|
||||
|
||||
```sh
|
||||
docker buildx build --platform linux/arm64 -t nxdns:arm64 -f deploy/docker/Dockerfile .
|
||||
```
|
||||
|
||||
This was run here and completed; add `--push` or `--load` to keep the result,
|
||||
since the default buildx driver leaves it in the build cache.
|
||||
|
||||
> Not verified on this host: the arm64 image was not started. Running it needs
|
||||
> an aarch64 machine or qemu binfmt emulation, neither of which is available
|
||||
> here.
|
||||
|
||||
## Publish the image to a registry
|
||||
|
||||
There is no registry push in CI on purpose: credentials and the choice of
|
||||
registry are infrastructure decisions, not this repository's. Publish by hand
|
||||
with `docker login <registry>`, then `docker tag nxdns
|
||||
<registry>/<owner>/nxdns:<tag>`, then `docker push
|
||||
<registry>/<owner>/nxdns:<tag>`.
|
||||
|
||||
> Not verified on this host: pushing needs credentials for a registry.
|
||||
Reference in New Issue
Block a user