Files
forust 0859479c0f
ci / lint-compose (push) Successful in 9s
ci / lint-actionlint (push) Successful in 4s
ci / lint-shellcheck (push) Successful in 7s
ci / lint-prettier (push) Successful in 12s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 9s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
renovate-ci / validate-renovate (push) Successful in 7s
ci / build (push) Failing after 14m22s
feat(ingress): replace traefik crowdsec plugin with firewall bouncer
Move L3 enforcement to the host firewall-bouncer (systemd, nftables): drop the Traefik plugin, its secrets volume and the crowdsec Middleware, remove bouncer refs from all IngressRoutes. Disable the http-generic-bf scenario (403-burst bans hurt legit automation under L3 enforcement). Add a Gateway API PoC for homepages prod and CrowdSec PrometheusRule alerts.
2026-09-30 20:14:25 +02:00
..
2026-09-26 00:15:01 +02:00

NetBox

NetBox for homelab documentation and visualization. Two runtimes are available:

Runtime Manifest Purpose
Docker compose.yaml Local stand on 127.0.0.1:8000 (no public exposure)
k8s k8s/ Homelab service on netbox.forust.xyz (and the internal name)

Both use the same image (netboxcommunity/netbox:v4.7-5.1.1) and Valkey for tasks plus a second logical database for caching. The Docker stand keeps its own PostgreSQL container, while the k8s deployment uses the shared database cluster (postgres.database.svc.cluster.local:5432, role/database netbox); only Valkey stays a per-service StatefulSet.

Docker Compose

cp .env.example .env
# replace CHANGE_ME
docker compose up -d

The UI is available at http://localhost:8000. The port is bound to 127.0.0.1 intentionally, so this stand is not exposed on the LAN or public interfaces.

The netbox service is also attached to the external proxy network and carries Traefik labels for netbox.forust.xyz and netbox.workstation.internal. Those labels only take effect while the Docker Traefik stack is running; it is currently stopped, and the live ingress path in this homelab is the k8s Traefik.

Inspect startup and health with:

docker compose ps
docker compose logs -f netbox

Stop it with docker compose down; data is kept in the named volumes netbox-postgres, netbox-media-files, netbox-reports-files, netbox-scripts-files and netbox-redis-data.

Kubernetes

k8s/ is deployed in the homelab cluster and serves netbox.forust.xyz publicly plus netbox.workstation.internal / netbox.gigaforust.internal internally. To rebuild it from scratch:

# 1. shared PostgreSQL: the password lives in the shared secret, NetBox keeps a copy
kubectl -n database patch secret postgres-shared-secrets \
  --type merge -p '{"stringData":{"NETBOX_DB_PASSWORD":"<same value>"}}'
kubectl -n database exec postgres17-0 -- psql -U postgres -d postgres \
  -c 'CREATE ROLE netbox LOGIN PASSWORD ...' -c 'CREATE DATABASE netbox OWNER netbox'

# 2. secrets first: the deploy workflow never applies *secret*.yaml
cp k8s/secrets.yaml.example k8s/secrets.yaml   # replace CHANGE_ME
kubectl apply -f k8s/secrets.yaml

# 3. manifests
kubectl apply -f k8s/

The shared cluster is reached at postgres.database.svc.cluster.local:5432. Its NetworkPolicy (postgres/k8s/network-policy.yaml) must list the netbox namespace or connections are dropped, and postgres/initdb/01-create-databases.sh already creates the role and database on a fresh data directory. NetBox has no PostgreSQL StatefulSet of its own — only netbox-valkey.

netbox.forust.xyz resolves to this host (78.98.72.122) through the DOMAINS list in the default/cfddns secret. cert-manager issues netbox-prod-tls with the letsencrypt-prod issuer, the internal route uses internal-wildcard-tls.

Resources are permanent again now that the first-boot migrations are complete: the web container reserves 100m/512Mi and is capped at 2 CPU/2Gi, the worker reserves 50m/256Mi and is capped at 1 CPU/1Gi, and Valkey reserves 25m/64Mi and is capped at 250m/256Mi. The deliberately generous CPU caps leave enough headroom for future schema migrations without letting one process consume the whole node.

The first start applies ~810 migrations, each in its own transaction with DDL and a commit; every later start is a no-op. The startup probe allows 15 minutes and progressDeadlineSeconds is 1800 for the same reason. Probes run inside the pod and explicitly set Host: netbox.forust.xyz; a kubelet httpGet.host field would replace the probe destination with that public hostname and bypass the pod.

Secrets

  • netbox/.env (compose) and netbox/k8s/secrets.yaml (k8s) are gitignored. Only .env.example and k8s/secrets.yaml.example are committed.
  • netbox/configuration/configuration.py is env-driven: hosts, database, Redis and the Django keys all come from the environment, so the same settings file works in both runtimes. The k8s copy lives in the netbox-settings ConfigMap (k8s/settings.yaml) and must be kept in sync with the file.
  • Rotating SECRET_KEY invalidates all sessions; rotating API_TOKEN_PEPPER_1 invalidates every API token.