Skip to main content

Homebox

A home inventory for what you own and where it lives.

Homebox (source) is a small, single-binary inventory app built for households rather than warehouses: items with a location and labels, purchase and warranty details, photos and attached documents. It ships as one container with an embedded SQLite database, which is exactly why it gets neither a Postgres cluster nor a public route here — it is an internal, low-write tool.

Alternatives considered​

Cloud Hosted

ToolOpen SourceFree TierNotes
SortlyNoLimitedInventory SaaS with a small free plan, paid beyond

Self Hosted

ToolOpen SourceFull FeaturesNotes
GrocyYesYesHousehold ERP centred on groceries and chores; inventory is one module of many
InvenTreeYesYesParts and stock management, built for makers and small manufacturing
Snipe-ITYesYesIT asset management with check-in/out; heavy for a household

Installation​

Architecture​

  • Deployment: Single homebox deployment in the homebox namespace, Recreate strategy, HBOX_MODE=production
  • Image: ghcr.io/sysadminsmedia/homebox, the -rootless variant (digest-pinned)
  • Storage: Longhorn PVC (homebox, 1Gi, longhorn-encrypted) on /data holding the SQLite database together with item photos and attachments; /tmp is an emptyDir. Deliberately no CNPG cluster for a small single-pod app
  • Networking: ClusterIP service on port 7745, HTTPRoute via the internal gateway on homebox.int.kueber.eu; HBOX_OPTIONS_TRUST_PROXY makes the app trust the gateway's TLS termination, without it Homebox sees plain HTTP and builds a wrong OIDC redirect
  • Email: outbound mail through the shared email component — the HBOX_MAILER_* variables read the allinkl SMTP credentials

Security​

  • The rootless image runs as uid 65532; the uid is pinned numerically because the image declares its USER by name and the kubelet cannot verify a named user against runAsNonRoot. readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, all capabilities dropped, fsGroup so the root-owned Longhorn mount is writable, no service-account token — the pod passes the restricted policies without any exception
  • Ingress only from the internal gateway's Envoy pods, so nothing here is reachable from the public internet, only from the home LAN and the NetBird mesh
  • Explicit egress on 587 and 465 to the internet for SMTP, RFC1918 excluded; the shared gateway-egress rule stays for the Keycloak issuer
  • The API-key pepper lives in the SOPS-encrypted homebox secret; the OIDC client secret comes from workspace-outputs, minted by the workspace that owns the Keycloak client
  • tier-2 priority class, 25m / 64Mi requested, 128Mi limit

Updates​

Managed by Renovate. The image is digest-pinned.

Data Management​

  • PVC: homebox (1Gi, Longhorn-encrypted), annotated k8up.io/backup: "true"
  • Backups: k8up Schedules to Hetzner and Backblaze (workload-talos-homebox-*) via restic. This is a file-level copy of a live SQLite file; a snapshot taken mid-write could in theory be inconsistent, which is accepted for a low-write app

User Management​

Login is in-app OIDC against Keycloak, client homebox in the kueber.eu realm, created by the app's workspace (spec.oidc, not gated) with the callback /api/v1/users/login/oidc/callback on the internal hostname; there is no gateway SecurityPolicy. Local accounts exist alongside: HBOX_OPTIONS_ALLOW_REGISTRATION ships as true so the first accounts can be created and is meant to be flipped to false afterwards — OIDC logins are unaffected by that flag, and the open signup is only ever LAN- or mesh-reachable. API keys are hashed with the HBOX_AUTH_API_KEY_PEPPER from the SOPS secret.

Configuration Management​

All settings are HBOX_* env vars in the talos patch and the email component's patch; secrets come from the SOPS-encrypted homebox secret and the synced SMTP credentials.

Usage​

Sign in with the Keycloak account, create locations and labels, then add items with their purchase details, photos and receipts. Warranty and maintenance information hangs off the item.

Cluster-specific deviations from the above live in the per-cluster README — see k8s/apps/homebox/talos/README.md.

Cluster Deployment​

homebox

Deviations from the standard app pattern:

  • In-app OIDC instead of a gateway SecurityPolicy — the patch wires HBOX_OIDC_ENABLED=true against the Keycloak client homebox, which the TofuWorkspace creates (workspace/patch-oidc.yaml, not gated) and whose secret arrives in workspace-outputs, so login happens inside Homebox and the internal route carries no Envoy OIDC policy. HBOX_OPTIONS_ALLOW_REGISTRATION ships as true so the first SSO users can create their account, and must be flipped to false afterwards. Internal ingress only, so the open signup is LAN/VPN-reachable at worst.
  • SQLite instead of CNPG — deliberately no database cluster for a small single-pod app; the SQLite file lives with the attachments on the 1Gi homebox PVC. k8up backs the PVC up file-level; a snapshot taken during a write could in theory be inconsistent, accepted for a low-write app.
  • Rootless image variant (-rootless tag, uid 65532) so the pod passes the restricted policies without a custom securityContext dance.
Kubernetes Metadata
  • Image: ghcr.io/sysadminsmedia/homebox:0.26.2-rootless@sha256:a41a3081ac724a2f4955b71b9294bd916b0e92035e4ea8438e84def74f4ae692
Rendered manifests (kustomize build)
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
labels:
app.kubernetes.io/name: homebox
name: homebox
namespace: homebox
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app.kubernetes.io/name: homebox
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: homebox
spec:
automountServiceAccountToken: false
containers:
- env:
- name: HBOX_OPTIONS_ALLOW_REGISTRATION
value: 'true'
- name: HBOX_OPTIONS_TRUST_PROXY
value: 'true'
- name: HBOX_OIDC_ENABLED
value: 'true'
- name: HBOX_OIDC_ISSUER_URL
value: https://keycloak.web.kueber.eu/realms/kueber.eu
- name: HBOX_OIDC_CLIENT_ID
value: homebox
- name: HBOX_OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
key: client-secret
name: workspace-outputs
- name: HBOX_AUTH_API_KEY_PEPPER
valueFrom:
secretKeyRef:
key: HBOX_AUTH_API_KEY_PEPPER
name: homebox
- name: HBOX_MAILER_HOST
valueFrom:
secretKeyRef:
key: host
name: allinkl-smtp-credentials
- name: HBOX_MAILER_PORT
valueFrom:
secretKeyRef:
key: port
name: allinkl-smtp-credentials
- name: HBOX_MAILER_USERNAME
valueFrom:
secretKeyRef:
key: username
name: allinkl-smtp-credentials
- name: HBOX_MAILER_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: allinkl-smtp-credentials
- name: HBOX_MAILER_FROM
valueFrom:
secretKeyRef:
key: from
name: allinkl-smtp-credentials
- name: HBOX_MODE
value: production
image: ghcr.io/sysadminsmedia/homebox:0.26.2-rootless@sha256:a41a3081ac724a2f4955b71b9294bd916b0e92035e4ea8438e84def74f4ae692
livenessProbe:
failureThreshold: 3
httpGet:
path: /api/v1/status
port: 7745
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: homebox
ports:
- containerPort: 7745
name: web
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /api/v1/status
port: 7745
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
memory: 128Mi
requests:
cpu: 25m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
startupProbe:
failureThreshold: 3
httpGet:
path: /api/v1/status
port: 7745
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
volumeMounts:
- mountPath: /data
name: homebox-data
- mountPath: /tmp
name: tmp
priorityClassName: tier-2
securityContext:
fsGroup: 65532
runAsGroup: 65532
runAsUser: 65532
volumes:
- name: homebox-data
persistentVolumeClaim:
claimName: homebox
- emptyDir: {}
name: tmp