The GitHub-hosted 'gha' cache backend is unreachable from the
self-hosted Gitea runner (dial tcp 172.18.0.2:44181 i/o timeout), which
failed the build step even though the image pushed successfully. Switch
cache-from/cache-to to a registry-backed cache stored as a :buildcache
tag in the same Gitea registry, which works on self-hosted runners.
Other Netbird peers connect fine, so the server and 51820/udp are OK --
the hang is specific to the Gitea runner. Replace the UDP port probes
with runner-side checks: /dev/net/tun presence, effective capabilities
(NET_ADMIN), WireGuard tooling/module, and a definitive 'ip tuntap add'
test. A runner container lacking TUN/NET_ADMIN cannot create the
WireGuard interface, which explains 'netbird up' timing out after the
control plane connects.
The Netbird server multiplexes management + signal + dashboard behind
Traefik on :443 (no separate :10000 signal port). The tunnel data plane
is WireGuard on 51820/udp and STUN on 3478/udp. Update the debug step to
probe those UDP ports plus a general outbound-UDP check (public STUN) to
determine whether the runner network blocks UDP egress -- the likely
cause of 'netbird up' timing out after the control plane connects fine.
Temporary diagnostic step before 'Netbird Connect' that checks secret
presence (masked), DNS resolution, HTTPS reachability to the management
API, and the signal server TCP port from the runner. Helps distinguish
DNS/connectivity issues from auth/registration failures behind the
recurring 'netbird up' DeadlineExceeded error. Marked continue-on-error
so it never blocks the build.
Switch the deploy job's Netbird Connect step to read management-url from
the NETBIRD_URL secret instead of a hardcoded value, so the URL can be
managed in the Gitea UI alongside the setup key.
Update the README to reflect the actual infrastructure: CI/CD runs on
Gitea Actions (not GitLab), images live in the Gitea container registry,
and the clone URL points to tea.ahmza.com. Also clarify the Blowfish
theme is a Hugo module, so no submodules are needed.
Switch the Caddy doc to the custom caddy-cf image
(ghcr.io/ahmzaa/caddy-cf) which bundles the Cloudflare DNS module, so
TLS certs are issued via the ACME DNS-01 challenge instead of HTTP-01.
This removes the need to expose ports 80/443 to the public internet,
which suits the Netbird-only infrastructure.
- Add (cloudflare) snippet + import cloudflare to both Caddyfiles
- Read CF_API_TOKEN from env (.env for Docker, Secret for K8s)
- Document token scope (Zone:DNS:Edit) and Cloudflare-hosted DNS
requirement
- Validated Caddyfile against the caddy-cf image (caddy adapt)
Document running Caddy in front of the SWS origin for both deployment
targets (Docker Compose on Gimli and the Kubernetes Pi cluster), with
Caddy handling automatic HTTPS, compression, and security headers.
Explains the layer separation: SWS serves static files + Cache-Control,
Caddy owns TLS/compression/security headers (matching sws.toml where
those are disabled). Caddyfile validated with 'caddy validate'.
The site sits behind a reverse proxy that already handles compression
and security headers. Disable those in SWS to avoid redundant work and
duplicate/conflicting headers, letting each layer own one concern.
- compression / compression-static: off (proxy compresses)
- security-headers: off (proxy sets them)
- cache-control-headers, page-fallback, health: kept (origin concerns)
Replace nginx:alpine-slim with the scratch-based static-web-server
image, reducing the final image size ~32MB -> ~23MB (28% smaller).
- Dockerfile: serve from static-web-server:2, copy site to /public
- sws.toml: mirror previous nginx behavior (compression, cache-control,
SPA index fallback) plus security headers and /health endpoint
- Remove now-unused nginx.conf