6f303dbbaa
Infrastructure:
- Stack now runs on K3s v1.34.6 HA (3 Hetzner CX33 nodes as managers)
- Traefik DaemonSet + hostNetwork replaces Caddy + ingress mesh
- All manifests in deploy-k3s/manifests/; Swarm config (deploy/) kept
temporarily for reference
Bug fixes surfaced during migration:
- Dockerfile: golang:1.24-alpine -> 1.25-alpine (go.mod requires 1.25)
- cache_service.go: remove sync.Once reassignment from inside Do()
callback (was causing 'unlock of unlocked mutex' fatal after
Redis Ping failure)
- router.go: relax CSP from 'default-src none' to 'default-src self'
+ allowlist fonts.googleapis.com so the marketing landing page CSS
actually loads in browsers
- deploy/scripts/deploy_prod.sh: use docker buildx with
--platform linux/amd64 so arm64 (Apple Silicon) dev machines produce
images runnable on x86_64 Hetzner nodes; fix array expansion under
set -u
- deploy/swarm-stack.prod.yml: fix secret source references to use
top-level aliases (the '\${X_SECRET}' form never actually resolved);
dozzle ports: long-form host_ip is rejected by Swarm, switched to
short-form (bound to 0.0.0.0 with UFW-based loopback restriction);
worker replicas 2 -> 1 (Asynq scheduler singleton)
- deploy-k3s/manifests/admin/deployment.yaml: probe path '/admin/' -> '/'
(Next.js serves at root; /admin/ returned 404 and killed pods);
startupProbe failureThreshold 12 -> 24
- deploy-k3s/manifests/pod-disruption-budgets.yaml: worker minAvailable
1 -> 0 (singleton)
- deploy-k3s/manifests/api/deployment.yaml: startupProbe failureThreshold
12 -> 48 (MigrateWithLock serializes across 3 replicas on first-boot;
real startup takes up to 240s)
- .gitignore: tighten 'api' -> '/api' (was matching deploy-k3s/manifests/api/
and admin/src/app/api/*, hiding legitimate files)
New files:
- deploy-k3s/manifests/traefik-helmchartconfig.yaml: DaemonSet +
hostNetwork override for k3s-bundled Traefik
- deploy-k3s/manifests/ingress/ingress-simple.yaml: plain Ingress
without TLS (CF Flexible SSL) and without middleware
- deploy-k3s/MIGRATION_NOTES.md: operator-facing migration log
Documentation:
- docs/deployment/ — full deployment book, 26 files, ~42k words:
- Part I Overview, infrastructure, orchestrator choice (Ch 0-2)
- Part II Networking, firewall, Cloudflare (Ch 3-4, 13)
- Part III Security, Traefik ingress (Ch 5-6)
- Part IV Services, DB, storage, secrets, registry (Ch 7-11)
- Part V Data flow, deploy process, observability, failures, runbook
(Ch 12, 14-17)
- Part VI Cost, Swarm postmortem, roadmap (Ch 18-20)
- Appendices: glossary, kubectl cheat sheet, file locations,
consolidated citations
- README.md: Production Deployment section replaced with pointer to
the book; Go version bumped to 1.25
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
203 lines
7.8 KiB
Markdown
203 lines
7.8 KiB
Markdown
# Appendix D — References & Citations
|
|
|
|
Every external link cited anywhere in this book, grouped by topic.
|
|
|
|
## Docker / Moby
|
|
|
|
- [moby/moby#52265 — Overlay ARP stale entries on 29.3.0 regression][moby-52265] (Chapter 19, primary root-cause citation)
|
|
- [moby/moby#51491 — DNS broken after `docker swarm init` on 29.0.0][moby-51491]
|
|
- [Dokploy#3480 — Traefik routes intermittently timeout due to stale VIP][dokploy-3480]
|
|
- [Mirantis: Commits to Long-Term Support for Swarm Through 2030][mirantis-swarm]
|
|
- [Better Stack: Hetzner Cloud Review 2026][bstack-swarm]
|
|
- [VirtualizationHowTo: Is Docker Swarm Still Safe in 2026?][vht-swarm]
|
|
- [bleevht: Where Docker Swarm Still Fits in 2026][bleevht-swarm]
|
|
- [Docker buildx multi-platform builds][buildx]
|
|
- [Compose specification][compose-spec]
|
|
|
|
## Kubernetes / k3s
|
|
|
|
- [K3s documentation home][k3s-docs]
|
|
- [K3s architecture][k3s-arch]
|
|
- [K3s requirements (networking ports)][k3s-reqs]
|
|
- [K3s advanced config — metrics server][k3s-metrics]
|
|
- [K3s HA datastore recovery][k3s-ha-recovery]
|
|
- [K3s storage — local-path provisioner][k3s-lp]
|
|
- [K3s Helm integration — HelmChartConfig][k3s-helm]
|
|
- [K3s Traefik customization][k3s-traefik]
|
|
- [K3s secrets encryption][k3s-secrets]
|
|
- [Kubernetes concepts — Services & Networking][k8s-net]
|
|
- [Kubernetes Ingress][k8s-ingress]
|
|
- [Kubernetes Deployments — rolling updates][rolling]
|
|
- [kubectl rollout][rollout]
|
|
- [kubectl cheat sheet][kubectl-cs]
|
|
- [Pod lifecycle + probes][probes]
|
|
- [Pod Security Standards][psa]
|
|
- [Kubernetes RBAC][rbac]
|
|
- [NetworkPolicy][netpol]
|
|
- [Ports and Protocols reference][k8s-ports]
|
|
- [metrics-server][ms]
|
|
|
|
## Traefik
|
|
|
|
- [Traefik v3 documentation][traefik]
|
|
- [Traefik Swarm provider][traefik-swarm]
|
|
- [Traefik migrate v2 → v3][traefik-v3]
|
|
|
|
## Cloudflare
|
|
|
|
- [IP ranges][cf-ips]
|
|
- [SSL modes explained][cf-ssl]
|
|
- [Origin CA certificates][cf-origin-ca]
|
|
- [DNS best practices][cf-dns]
|
|
- [Free plan][cf-free]
|
|
|
|
## Hetzner
|
|
|
|
- [Hetzner Cloud][hetzner-cloud]
|
|
- [Hetzner price adjustment 2026-04-01][hetzner-prices]
|
|
- [Hetzner rescue system][hetzner-rescue]
|
|
- [hetzner-k3s tool][hetzner-k3s]
|
|
|
|
## Neon / Postgres
|
|
|
|
- [Neon docs][neon-docs]
|
|
- [Neon pricing][neon-pricing]
|
|
- [Neon usage-based pricing announcement][neon-blog]
|
|
- [Neon connect from any app][neon-connect]
|
|
- [Postgres advisory locks][pg-locks]
|
|
- [GORM AutoMigrate][gorm-automigrate]
|
|
|
|
## Backblaze B2
|
|
|
|
- [B2 documentation][b2-docs]
|
|
- [B2 S3-compatible API][b2-s3]
|
|
- [B2 pricing][b2-pricing]
|
|
- [minio-go SDK][minio-go]
|
|
- [S3 path-style vs virtual-hosted addressing][s3-style]
|
|
|
|
## Gitea
|
|
|
|
- [Gitea container registry docs][gitea-cr]
|
|
|
|
## CNI / Networking
|
|
|
|
- [Flannel VXLAN backend][flannel-vxlan]
|
|
- [CoreDNS Kubernetes plugin][coredns-k8s]
|
|
- [IPVS mode for kube-proxy deep dive][ipvs]
|
|
- [VXLAN RFC 7348][vxlan-rfc]
|
|
- [Kubernetes NetworkPolicy][netpol]
|
|
|
|
## Security tools
|
|
|
|
- [cosign (image signing)][cosign]
|
|
- [Loki (logs)][loki]
|
|
- [Stern (multi-pod log tailing)][stern]
|
|
- [fail2ban][fail2ban]
|
|
|
|
## Asynq
|
|
|
|
- [Asynq documentation][asynq]
|
|
- [Asynq periodic tasks (scheduler limitations)][asynq-sched]
|
|
|
|
## Miscellaneous
|
|
|
|
- [Let's Encrypt][le]
|
|
- [UFW man page][ufw-man]
|
|
- [SSH hardening guide][ssh-guide]
|
|
- [pg_dump][pg-dump]
|
|
|
|
---
|
|
|
|
## Link definitions
|
|
|
|
<!-- Docker / Moby -->
|
|
[moby-52265]: https://github.com/moby/moby/issues/52265
|
|
[moby-51491]: https://github.com/moby/moby/issues/51491
|
|
[dokploy-3480]: https://github.com/Dokploy/dokploy/issues/3480
|
|
[mirantis-swarm]: https://www.mirantis.com/blog/mirantis-guarantees-long-term-support-for-swarm/
|
|
[bstack-swarm]: https://betterstack.com/community/guides/web-servers/hetzner-cloud-review/
|
|
[vht-swarm]: https://www.virtualizationhowto.com/2026/03/is-docker-swarm-still-safe-in-2026/
|
|
[bleevht-swarm]: https://bleevht.substack.com/p/where-docker-swarm-still-fits-in
|
|
[buildx]: https://docs.docker.com/build/buildx/
|
|
[compose-spec]: https://docs.docker.com/reference/compose-file/
|
|
|
|
<!-- Kubernetes / k3s -->
|
|
[k3s-docs]: https://docs.k3s.io/
|
|
[k3s-arch]: https://docs.k3s.io/architecture
|
|
[k3s-reqs]: https://docs.k3s.io/installation/requirements#networking
|
|
[k3s-metrics]: https://docs.k3s.io/advanced#enabling-metrics-server
|
|
[k3s-ha-recovery]: https://docs.k3s.io/datastore/ha-embedded#new-cluster-with-embedded-db
|
|
[k3s-lp]: https://docs.k3s.io/storage#setting-up-the-local-storage-provider
|
|
[k3s-helm]: https://docs.k3s.io/helm#customizing-packaged-components-with-helmchartconfig
|
|
[k3s-traefik]: https://docs.k3s.io/networking/networking-services#traefik-ingress-controller
|
|
[k3s-secrets]: https://docs.k3s.io/security/secrets-encryption
|
|
[k8s-net]: https://kubernetes.io/docs/concepts/services-networking/
|
|
[k8s-ingress]: https://kubernetes.io/docs/concepts/services-networking/ingress/
|
|
[rolling]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#rolling-update-deployment
|
|
[rollout]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#rollout
|
|
[kubectl-cs]: https://kubernetes.io/docs/reference/kubectl/cheatsheet/
|
|
[probes]: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-lifecycle
|
|
[psa]: https://kubernetes.io/docs/concepts/security/pod-security-standards/
|
|
[rbac]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
|
|
[netpol]: https://kubernetes.io/docs/concepts/services-networking/network-policies/
|
|
[k8s-ports]: https://kubernetes.io/docs/reference/networking/ports-and-protocols/
|
|
[ms]: https://github.com/kubernetes-sigs/metrics-server
|
|
|
|
<!-- Traefik -->
|
|
[traefik]: https://doc.traefik.io/traefik/v3.6/
|
|
[traefik-swarm]: https://doc.traefik.io/traefik/providers/swarm/
|
|
[traefik-v3]: https://doc.traefik.io/traefik/migrate/v2-to-v3-details/
|
|
|
|
<!-- Cloudflare -->
|
|
[cf-ips]: https://www.cloudflare.com/ips/
|
|
[cf-ssl]: https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/
|
|
[cf-origin-ca]: https://developers.cloudflare.com/ssl/origin-configuration/origin-ca/
|
|
[cf-dns]: https://developers.cloudflare.com/dns/
|
|
[cf-free]: https://www.cloudflare.com/plans/free/
|
|
|
|
<!-- Hetzner -->
|
|
[hetzner-cloud]: https://www.hetzner.com/cloud/
|
|
[hetzner-prices]: https://docs.hetzner.com/general/infrastructure-and-availability/price-adjustment/
|
|
[hetzner-rescue]: https://docs.hetzner.com/cloud/servers/getting-started/enabling-rescue-system/
|
|
[hetzner-k3s]: https://github.com/vitobotta/hetzner-k3s
|
|
|
|
<!-- Neon / Postgres -->
|
|
[neon-docs]: https://neon.com/docs/introduction
|
|
[neon-pricing]: https://neon.com/pricing
|
|
[neon-blog]: https://neon.com/blog/new-usage-based-pricing
|
|
[neon-connect]: https://neon.com/docs/connect/connect-from-any-app
|
|
[pg-locks]: https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS
|
|
[gorm-automigrate]: https://gorm.io/docs/migration.html
|
|
|
|
<!-- B2 -->
|
|
[b2-docs]: https://www.backblaze.com/docs/
|
|
[b2-s3]: https://www.backblaze.com/docs/cloud-storage-s3-compatible-api
|
|
[b2-pricing]: https://www.backblaze.com/cloud-storage/pricing
|
|
[minio-go]: https://github.com/minio/minio-go
|
|
[s3-style]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html
|
|
|
|
<!-- Gitea -->
|
|
[gitea-cr]: https://docs.gitea.com/usage/packages/container
|
|
|
|
<!-- CNI -->
|
|
[flannel-vxlan]: https://github.com/flannel-io/flannel/blob/master/Documentation/backends.md#vxlan
|
|
[coredns-k8s]: https://coredns.io/plugins/kubernetes/
|
|
[ipvs]: https://kubernetes.io/blog/2018/07/09/ipvs-based-in-cluster-load-balancing-deep-dive/
|
|
[vxlan-rfc]: https://datatracker.ietf.org/doc/html/rfc7348
|
|
|
|
<!-- Security tools -->
|
|
[cosign]: https://github.com/sigstore/cosign
|
|
[loki]: https://grafana.com/oss/loki/
|
|
[stern]: https://github.com/stern/stern
|
|
[fail2ban]: https://www.fail2ban.org/
|
|
|
|
<!-- Asynq -->
|
|
[asynq]: https://github.com/hibiken/asynq
|
|
[asynq-sched]: https://github.com/hibiken/asynq/wiki/Periodic-Tasks
|
|
|
|
<!-- Misc -->
|
|
[le]: https://letsencrypt.org/
|
|
[ufw-man]: https://manpages.ubuntu.com/manpages/noble/en/man8/ufw.8.html
|
|
[ssh-guide]: https://linux-audit.com/audit-and-harden-your-ssh-configuration/
|
|
[pg-dump]: https://www.postgresql.org/docs/current/app-pgdump.html
|