netbox: add NetBox 4.6.8 with postgres, valkey and rqworker

Five components under applications/netbox: the web pod, an rqworker,
a daily housekeeping CronJob, postgres 18 and two valkey instances.
The task queue runs appendonly on its own PVC so queued jobs survive a
restart, while the cache instance is disposable.

Worker and cronjob override args rather than command, which replaces
CMD while keeping tini as the entrypoint, so only the web pod runs
migrations. Media, reports and scripts share one RWX PVC via subPaths
because both the web pod and the worker mount them.

Exposed on the internal gateway only.
This commit is contained in:
2026-08-27 20:46:34 +02:00
parent 2d078e10c0
commit cfd9317086
24 changed files with 597 additions and 8 deletions
+2
View File
@@ -35,6 +35,7 @@ The repository is split into three top-level areas. Within each, there is one di
│ ├── minio-aistor/ # S3-compatible object storage │ ├── minio-aistor/ # S3-compatible object storage
│ ├── n8n/ # Workflow automation │ ├── n8n/ # Workflow automation
│ ├── netbootxyz/ # Network boot service │ ├── netbootxyz/ # Network boot service
│ ├── netbox/ # IPAM and DCIM source of truth
│ ├── ollama/ # Local LLM runner │ ├── ollama/ # Local LLM runner
│ ├── openwebui/ # Web UI for AI models │ ├── openwebui/ # Web UI for AI models
│ ├── outline/ # Knowledge base / wiki │ ├── outline/ # Knowledge base / wiki
@@ -105,6 +106,7 @@ The repository is split into three top-level areas. Within each, there is one di
- **Technitium**: DNS server with web interface - **Technitium**: DNS server with web interface
- **Azure DDNS Python**: Dynamic DNS updater for Azure DNS - **Azure DDNS Python**: Dynamic DNS updater for Azure DNS
- **NetbootXYZ**: Network boot service for OS installation - **NetbootXYZ**: Network boot service for OS installation
- **NetBox**: IPAM and DCIM source of truth for the network
- **VictoriaMetrics**: Metrics collection and monitoring stack - **VictoriaMetrics**: Metrics collection and monitoring stack
### External Service Integration ### External Service Integration
@@ -0,0 +1,37 @@
apiVersion: batch/v1
kind: CronJob
metadata:
namespace: netbox
name: netbox-housekeeping
spec:
schedule: "15 3 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
securityContext:
runAsUser: 999
runAsGroup: 0
fsGroup: 0
containers:
- name: netbox-housekeeping
image: netboxcommunity/netbox:v4.6.8-5.0.2
args:
- /opt/netbox/venv/bin/python
- /opt/netbox/netbox/manage.py
- housekeeping
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "512Mi"
cpu: "500m"
envFrom:
- secretRef:
name: netbox-app-secrets
+46
View File
@@ -0,0 +1,46 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: netbox-route
namespace: netbox
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: gateway-internal
namespace: envoy-gateway
sectionName: netbox
hostnames:
- "netbox.jsme.be"
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set:
- name: X-Forwarded-Host
value: "netbox.jsme.be"
- name: X-Forwarded-Proto
value: https
- type: ResponseHeaderModifier
responseHeaderModifier:
set:
- name: Strict-Transport-Security
value: "max-age=31536000; includeSubDomains"
- name: X-Content-Type-Options
value: nosniff
- name: X-Frame-Options
value: SAMEORIGIN
- name: Referrer-Policy
value: strict-origin-when-cross-origin
- name: Permissions-Policy
value: "camera=(), microphone=(), geolocation=(), payment=()"
backendRefs:
- name: netbox
port: 8080
kind: Service
group: ""
weight: 1
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: netbox
@@ -0,0 +1,70 @@
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: netbox
name: netbox
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: netbox
template:
metadata:
labels:
app: netbox
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 60
securityContext:
runAsUser: 999
runAsGroup: 0
fsGroup: 0
containers:
- name: netbox
image: netboxcommunity/netbox:v4.6.8-5.0.2
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "2000m"
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: netbox-app-secrets
volumeMounts:
- name: netbox-data
mountPath: /opt/netbox/netbox/media
subPath: media
- name: netbox-data
mountPath: /opt/netbox/netbox/reports
subPath: reports
- name: netbox-data
mountPath: /opt/netbox/netbox/scripts
subPath: scripts
startupProbe:
httpGet:
path: /login/
port: 8080
periodSeconds: 10
# Generous: the entrypoint runs migrations before granian binds.
failureThreshold: 60
readinessProbe:
httpGet:
path: /login/
port: 8080
periodSeconds: 15
livenessProbe:
httpGet:
path: /login/
port: 8080
periodSeconds: 30
failureThreshold: 5
volumes:
- name: netbox-data
persistentVolumeClaim:
claimName: netbox-data-pvc
@@ -0,0 +1,25 @@
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalSecret
metadata:
name: netbox-app-secrets
namespace: infisical
spec:
hostAPI: https://infisical.jsme.be
resyncInterval: 30
authentication:
kubernetesAuth:
identityId: "5458def7-7c63-40d2-95da-41c614d0933a"
serviceAccountRef:
name: infisical-auth
namespace: infisical
autoCreateServiceAccountToken: true
secretsScope:
projectSlug: "kubernetes"
envSlug: "prod"
secretsPath: "/netbox/netbox"
# Non-recursive: keys are already named as the container expects.
recursive: false
managedSecretReference:
secretName: netbox-app-secrets
secretNamespace: netbox
secretType: Opaque
@@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: netbox
name: netbox-data-pvc
spec:
# RWX: shared by the web pod and the rqworker pod.
accessModes:
- ReadWriteMany
storageClassName: longhorn-static
resources:
requests:
storage: 5Gi
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
namespace: netbox
name: netbox
spec:
selector:
app: netbox
ports:
- protocol: TCP
port: 8080
targetPort: 8080
type: ClusterIP
@@ -0,0 +1,25 @@
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalSecret
metadata:
name: netbox-postgres-secrets
namespace: infisical
spec:
hostAPI: https://infisical.jsme.be
resyncInterval: 30
authentication:
kubernetesAuth:
identityId: "5458def7-7c63-40d2-95da-41c614d0933a"
serviceAccountRef:
name: infisical-auth
namespace: infisical
autoCreateServiceAccountToken: true
secretsScope:
projectSlug: "kubernetes"
envSlug: "prod"
secretsPath: "/netbox/postgres"
# Non-recursive: keys are already named as the container expects.
recursive: false
managedSecretReference:
secretName: netbox-postgres-secrets
secretNamespace: netbox
secretType: Opaque
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: netbox
name: netbox-postgres-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn-static
resources:
requests:
storage: 10Gi
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: netbox-postgres
namespace: netbox
spec:
selector:
app: netbox-postgres
type: ClusterIP
ports:
- name: netbox-postgres-port
protocol: TCP
port: 5432
targetPort: 5432
@@ -0,0 +1,45 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: netbox
name: netbox-postgres
spec:
replicas: 1
serviceName: netbox-postgres
selector:
matchLabels:
app: netbox-postgres
template:
metadata:
labels:
app: netbox-postgres
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 60
containers:
- name: netbox-postgres
image: postgres:18
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "1000m"
ports:
- containerPort: 5432
volumeMounts:
- name: netbox-postgres-data
mountPath: /var/lib/postgresql/data
envFrom:
- secretRef:
name: netbox-postgres-secrets
readinessProbe:
exec:
command: ["sh", "-c", "pg_isready -q -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\""]
initialDelaySeconds: 10
periodSeconds: 10
volumes:
- name: netbox-postgres-data
persistentVolumeClaim:
claimName: netbox-postgres-pvc
@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: netbox
name: netbox-redis-cache
spec:
replicas: 1
selector:
matchLabels:
app: netbox-redis-cache
template:
metadata:
labels:
app: netbox-redis-cache
spec:
restartPolicy: Always
containers:
- name: netbox-redis-cache
image: valkey/valkey:9.1-alpine
# Cache only: no persistence, contents are disposable.
command: ["sh", "-c", "exec valkey-server --requirepass \"$REDIS_PASSWORD\""]
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
ports:
- containerPort: 6379
envFrom:
- secretRef:
name: netbox-redis-cache-secrets
readinessProbe:
exec:
command: ["sh", "-c", "valkey-cli --pass \"$REDIS_PASSWORD\" ping | grep -q PONG"]
initialDelaySeconds: 5
periodSeconds: 10
@@ -0,0 +1,25 @@
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalSecret
metadata:
name: netbox-redis-cache-secrets
namespace: infisical
spec:
hostAPI: https://infisical.jsme.be
resyncInterval: 30
authentication:
kubernetesAuth:
identityId: "5458def7-7c63-40d2-95da-41c614d0933a"
serviceAccountRef:
name: infisical-auth
namespace: infisical
autoCreateServiceAccountToken: true
secretsScope:
projectSlug: "kubernetes"
envSlug: "prod"
secretsPath: "/netbox/redis-cache"
# Non-recursive: keys are already named as the container expects.
recursive: false
managedSecretReference:
secretName: netbox-redis-cache-secrets
secretNamespace: netbox
secretType: Opaque
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: netbox-redis-cache
namespace: netbox
spec:
selector:
app: netbox-redis-cache
type: ClusterIP
ports:
- name: netbox-redis-cache-port
protocol: TCP
port: 6379
targetPort: 6379
@@ -0,0 +1,25 @@
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalSecret
metadata:
name: netbox-redis-secrets
namespace: infisical
spec:
hostAPI: https://infisical.jsme.be
resyncInterval: 30
authentication:
kubernetesAuth:
identityId: "5458def7-7c63-40d2-95da-41c614d0933a"
serviceAccountRef:
name: infisical-auth
namespace: infisical
autoCreateServiceAccountToken: true
secretsScope:
projectSlug: "kubernetes"
envSlug: "prod"
secretsPath: "/netbox/redis"
# Non-recursive: keys are already named as the container expects.
recursive: false
managedSecretReference:
secretName: netbox-redis-secrets
secretNamespace: netbox
secretType: Opaque
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: netbox
name: netbox-redis-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn-static
resources:
requests:
storage: 1Gi
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: netbox-redis
namespace: netbox
spec:
selector:
app: netbox-redis
type: ClusterIP
ports:
- name: netbox-redis-port
protocol: TCP
port: 6379
targetPort: 6379
@@ -0,0 +1,46 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: netbox
name: netbox-redis
spec:
replicas: 1
serviceName: netbox-redis
selector:
matchLabels:
app: netbox-redis
template:
metadata:
labels:
app: netbox-redis
spec:
restartPolicy: Always
containers:
- name: netbox-redis
image: valkey/valkey:9.1-alpine
# Task queue: appendonly so queued background jobs survive a restart.
command: ["sh", "-c", "exec valkey-server --appendonly yes --requirepass \"$REDIS_PASSWORD\""]
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
ports:
- containerPort: 6379
envFrom:
- secretRef:
name: netbox-redis-secrets
volumeMounts:
- name: netbox-redis-data
mountPath: /data
readinessProbe:
exec:
command: ["sh", "-c", "valkey-cli --pass \"$REDIS_PASSWORD\" ping | grep -q PONG"]
initialDelaySeconds: 5
periodSeconds: 10
volumes:
- name: netbox-redis-data
persistentVolumeClaim:
claimName: netbox-redis-pvc
@@ -0,0 +1,54 @@
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: netbox
name: netbox-worker
spec:
replicas: 1
selector:
matchLabels:
app: netbox-worker
template:
metadata:
labels:
app: netbox-worker
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 60
securityContext:
runAsUser: 999
runAsGroup: 0
fsGroup: 0
containers:
- name: netbox-worker
image: netboxcommunity/netbox:v4.6.8-5.0.2
# Overrides CMD only, so tini stays the entrypoint and migrations
# are left to the web pod.
args:
- /opt/netbox/venv/bin/python
- /opt/netbox/netbox/manage.py
- rqworker
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "1000m"
envFrom:
- secretRef:
name: netbox-app-secrets
volumeMounts:
- name: netbox-data
mountPath: /opt/netbox/netbox/media
subPath: media
- name: netbox-data
mountPath: /opt/netbox/netbox/reports
subPath: reports
- name: netbox-data
mountPath: /opt/netbox/netbox/scripts
subPath: scripts
volumes:
- name: netbox-data
persistentVolumeClaim:
claimName: netbox-data-pvc
@@ -0,0 +1,23 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: netbox
namespace: argocd
spec:
project: default
source:
repoURL: https://gitea.jsme.be/Jeffrey/Kubernetes-Manifests.git
path: applications/netbox
targetRevision: HEAD
directory:
recurse: true
destination:
server: https://kubernetes.default.svc
namespace: netbox
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ServerSideApply=true
- PrunePropagationPolicy=foreground
+7 -8
View File
@@ -60,8 +60,7 @@ resources:
- pelican/pelican-wings-mc-certificate.yaml - pelican/pelican-wings-mc-certificate.yaml
- pelican/pelican-wings-ark-certificate.yaml - pelican/pelican-wings-ark-certificate.yaml
- outline/outline-certificate.yaml - outline/outline-certificate.yaml
# netbox: uncomment together with the patch below once infra/envoy-gateway/netbox/ is committed - netbox/netbox-certificate.yaml
# - netbox/netbox-certificate.yaml
patches: patches:
# Internal gateway patches # Internal gateway patches
@@ -293,12 +292,12 @@ patches:
kind: Gateway kind: Gateway
name: gateway-internal name: gateway-internal
path: outline/outline-gateway-patch.yaml path: outline/outline-gateway-patch.yaml
# - target: - target:
# group: gateway.networking.k8s.io group: gateway.networking.k8s.io
# version: v1 version: v1
# kind: Gateway kind: Gateway
# name: gateway-internal name: gateway-internal
# path: netbox/netbox-gateway-patch.yaml path: netbox/netbox-gateway-patch.yaml
# Public gateway patches # Public gateway patches
- target: - target:
@@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: netbox-tls
namespace: envoy-gateway
spec:
secretName: netbox-tls
privateKey:
rotationPolicy: Always
issuerRef:
name: azure-dns
kind: ClusterIssuer
dnsNames:
- netbox.jsme.be
@@ -0,0 +1,19 @@
- op: add
path: /spec/listeners/-
value:
name: netbox
protocol: HTTPS
port: 443
hostname: "netbox.jsme.be"
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: netbox
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: netbox-tls
namespace: envoy-gateway