- Go 54.2%
- Dockerfile 45.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
CoreDNS / ⌚ Build and Deploy (push) Successful in 14m34s
It was annotated as picking up hardened base image updates, but the DHI tier in use gets no new date-stamped tags, so the base is effectively fixed at the pin in the Dockerfile. What the rebuild does refresh is the Go toolchain: setup-go tracks stable, so a Go patch release is compiled in without waiting for the next commit. Worth recording, or the cron reads as pointless and gets removed. |
||
| .forgejo/workflows | ||
| cmd/coredns | ||
| deploy/kubernetes | ||
| .dockerignore | ||
| .gitignore | ||
| compose.yaml | ||
| Corefile.example | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
coredns
This homelab's CoreDNS distribution: upstream CoreDNS plus the external plugins it needs, built into one hardened container image.
CoreDNS has no runtime plugin loading, so carrying an external plugin means building your own binary. This repository is that build — the plugins themselves live in their own repositories and are consumed as ordinary Go modules.
| Plugin | Source | Corefile position |
|---|---|---|
nextdns |
user00265/coredns-nextdns | after forward |
Everything in upstream's plugin.cfg comes along unchanged — cache,
forward, log, errors, health, ready, prometheus, hosts,
template, view, metadata and the rest. A plugin being compiled in does
not make it active; the Corefile decides that.
Image
scm.nullnetwork.cc/homelab/coredns:latest
linux/amd64 and linux/arm64. Built on the Docker Hardened Images static
base: distroless, nonroot 65532, no shell and no package manager. The binary
is CGO_ENABLED=0 and stripped.
No setcap, no capabilities, no root. Binding 53, 443 or 853 as an
unprivileged user is left to net.ipv4.ip_unprivileged_port_start=0, which
compose.yaml sets.
Ports
| Port | Needs | |
|---|---|---|
| 53/udp, 53/tcp | plain DNS | |
| 443/tcp | DoH (https://), or gRPC (grpc://) |
TLS material |
| 443/udp | DoH over HTTP/3 | TLS material |
| 853/tcp | DoT (tls://) |
TLS material |
| 853/udp | DoQ (quic://) — RFC 9250 shares the DoT port |
TLS material |
| 8080/tcp | health |
health in the Corefile |
| 8181/tcp | ready |
ready in the Corefile |
| 9153/tcp | prometheus |
prometheus :9153 — the plugin defaults to localhost |
pprof (localhost:6053) is deliberately not exposed: it serves heap and
goroutine dumps. Map it yourself if you need it.
CoreDNS does not do ACME. The tls plugin reads certificate files you provide.
Running
Docker — see compose.yaml. Mount a single Corefile read-only; the image
bakes none in, and CoreDNS keeps no state worth a volume.
cp Corefile.example Corefile # then edit in your profile ID
docker compose up -d
Kubernetes — see deploy/kubernetes/coredns.yaml. It listens high in the
container and lets the Service map 53 onto it, because
net.ipv4.ip_unprivileged_port_start is an unsafe sysctl that kubelet
rejects unless started with --allowed-unsafe-sysctls. The manifest documents
the direct-bind alternative if you control kubelet.
Note externalTrafficPolicy: Local in the Service: the nextdns plugin
attributes queries by source address, and the default policy SNATs it to a node
address, which would collapse every device into one.
Building
go build -o coredns ./cmd/coredns
cmd/coredns/main.go imports core/plugin for the stock set, imports each
external plugin, and splices its directive into dnsserver.Directives at the
right position — the equivalent of adding a plugin.cfg line, without needing
a CoreDNS checkout or its generator.
To add a plugin: import it in main.go and add an entry to external
naming the stock directive it should follow. The tests check that every entry
lands in the right place.
Releases
There are none, deliberately. What an image contains is decided by go.mod —
which CoreDNS, which plugins — so a version of its own here would be a third
number with no independent meaning. Every push to master publishes, and a
nightly rebuild picks up hardened base image updates.
Image tags describe the contents instead:
| tag | |
|---|---|
latest |
moves with master |
1.14.7 |
the CoreDNS carried; moves as plugins and the base image update |
1.14.7-abc1234 |
immutable and exact — pin or roll back to this |
The CoreDNS version is read out of go.mod by the workflow rather than written
by hand, so it cannot drift.
The Dockerfile does the cross-compiling itself: the build stage is pinned to
$BUILDPLATFORM and Go targets $TARGETARCH from there, so a multi-arch build
never runs anything under emulation. See .forgejo/workflows/.