Use case · Home inventory

Homebox self hosted: a home inventory on a machine of your own

To self-host Homebox, run the official ghcr.io/sysadminsmedia/homebox container pinned to a release tag, keep its /data directory on the host, create your account over an SSH tunnel, then switch self-registration off before any proxy publishes it. Homebox is a Go binary with an embedded web UI and an SQLite file, so the work is getting sign-up, TLS and backups right rather than sizing. The steps target release 0.27.1 on Ubuntu 24.04 or newer.

See the machines

Which Homebox to install

GitHub marks the original hay-kot/homebox repository archived; its last push was in June 2024. Development continues at sysadminsmedia/homebox, whose release 0.27.1 (23 October 2026) is a security release. Its quick-start presents the move from hay-kot as an image swap, but from 0.26.0 on that alone will not start: set HBOX_AUTH_API_KEY_PEPPER to 32 bytes or more, and before using the rootless image, chown the data folder to 65532, as the install page shows.

ItemCurrent project
Imageghcr.io/sysadminsmedia/homebox, also sysadminsmedia/homebox on Docker Hub
Tagslatest and version tags, each with -rootless and -hardened variants
Platformslinux/amd64 and linux/arm64; no 32-bit ARM image
DatabaseSQLite at /data/homebox.db in WAL mode, or PostgreSQL
LicenceAGPL-3.0

The hardened tag is distroless, with no shell and no MQTT client. This page uses rootless, which runs as UID 65532 (Dockerfile.rootless) and keeps a shell for troubleshooting. Platforms come from the three publish workflows, one each for the regular, rootless and hardened images.

Sizing, and the machine to put it on

The README puts idle memory under 50 MB for the whole container and publishes no CPU or disk minimum. Our reading: the disk is what grows, because photos, receipts and manuals sit beside the database.

On our dedicated server range, a Moonshot cartridge is the one-service-per-machine class; of those, only the m510 and m710x add their own out-of-band console, while the rest offer remote power cycling alone. A single-socket rack server suits Homebox sharing the box with other services, and every racked one has a full console. The arm64 image also runs on the built-to-order Ampere Altra line on ARM. Exclude the X-Gene cartridge: it boots only Ubuntu 16.04 and 18.04, too old for the Docker Engine checked below. Once payment is approved, a ready server goes live in about 30 minutes; built-to-order lines take 4 to 24 hours.

Installing: firewall, pepper, loopback-only port

Install Docker Engine and the Compose plugin from Docker's apt repository, save the script below as /root/homebox-setup.sh and run sudo bash /root/homebox-setup.sh. It enables ufw with SSH allowed, stops unless Docker Engine is 28 or newer, refuses a second run, writes the pepper into a file created at mode 600, then starts the container.

set -euo pipefail
sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
ufw allow OpenSSH
ufw --force enable
v=$(docker version --format '{{.Server.Version}}')
if [ "${v%%.*}" -lt 28 ]; then echo "Docker Engine $v: need 28 or newer"; exit 1; fi
d=/srv/homebox
if [ -e "$d/.env" ]; then echo "$d/.env exists: refusing to replace the pepper"; exit 1; fi
install -d -m 750 "$d"
install -d -o 65532 -g 65532 -m 750 "$d/data"
p=$(openssl rand -base64 48)
if [ "${#p}" -lt 32 ]; then echo "pepper not generated"; exit 1; fi
install -m 600 /dev/null "$d/.env"
printf 'HBOX_AUTH_API_KEY_PEPPER=%s\n' "$p" > "$d/.env"
cat > "$d/compose.yaml" <<'EOF'
services:
  homebox:
    image: ghcr.io/sysadminsmedia/homebox:0.27.1-rootless
    container_name: homebox
    restart: unless-stopped
    env_file: /srv/homebox/.env
    environment:
      - HBOX_OPTIONS_ALLOW_REGISTRATION=true
      - HBOX_OPTIONS_TRUST_PROXY=true
      - HBOX_OPTIONS_ALLOW_ANALYTICS=false
      - HBOX_WEB_MAX_UPLOAD_SIZE=10
    volumes:
      - /srv/homebox/data:/data
    ports:
      - "127.0.0.1:3100:7745"
EOF
docker compose -f "$d/compose.yaml" up -d

The docs publish 3100:7745 with no address, which listens on 0.0.0.0 and [::]; Docker's rules let that past ufw on IPv4, and on IPv6 once Docker's own IPv6 networking is on. The 127.0.0.1: prefix keeps the port local, a restriction Docker Engine applies from version 28 onward, hence the check. The configuration page marks HBOX_AUTH_API_KEY_PEPPER required: under 32 bytes the binary refuses to start, and rotating it invalidates every API key. Root can still read it through docker inspect.

Our choices: trust-proxy is on now because only loopback reaches the port, analytics stay off (the default), and registration is set to true explicitly so the next step can flip it.

Your account first, then sign-up off

Homebox has no default login; the quick-start says to register your first user. In service_user.go, each registration without an invitation creates a new group its user owns, so an open form lets strangers keep inventories on your disk.

  1. On your computer: ssh -L 3100:127.0.0.1:3100 you@your-server, open http://localhost:3100 and register with a long password.
  2. On the server: sudo sed -i 's/HBOX_OPTIONS_ALLOW_REGISTRATION=true/HBOX_OPTIONS_ALLOW_REGISTRATION=false/' /srv/homebox/compose.yaml && sudo grep -q 'ALLOW_REGISTRATION=false' /srv/homebox/compose.yaml && sudo docker compose -f /srv/homebox/compose.yaml up -d
  3. Add household members with an invite link (Profile, Generate Invite Link). v1_ctrl_user.go still accepts sign-ups that carry an invitation token.

nginx and a certificate

/api/v1/status reports the sign-up switch as allowRegistration (controller.go), so the proxy install reads it first. Point an A record (and AAAA, with IPv6) at the server, then run curl -s http://127.0.0.1:3100/api/v1/status | grep -q '"allowRegistration":false' && sudo apt install -y nginx certbot python3-certbot-nginx. If nothing installs, sign-up is open or Homebox was restarting: check, then retry.

server {
    listen 80;
    listen [::]:80;
    server_name inventory.example.com;
    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:3100;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400;
    }
}

Write it to /etc/nginx/sites-available/homebox. It is the proxy page's nginx block with three changes of ours: proxy_pass targets the loopback port instead of localhost:7745, listen [::]:80 adds IPv6, and client_max_body_size 10m matches the upload limit, since nginx refuses bodies over 1 MB by default. Then sudo ln -s /etc/nginx/sites-available/homebox /etc/nginx/sites-enabled/ && sudo nginx -t && sudo systemctl reload nginx, sudo ufw allow 80,443/tcp (ufw filters host processes on both families), and sudo certbot --nginx -d inventory.example.com.

Labels, QR codes and attachments

Per the tips page, items and locations have a QR code button, a built-in reader scans codes from a phone, the Tools page generates label sheets by asset ID, and an item's Labels button makes a one-off label for a label maker. Asset IDs auto-increment as 000-001; search #000-001 to find one. Labels encode a URL built from your Homebox address, and the proxy page warns labels may misbehave without trust-proxy.

HBOX_LABEL_MAKER_PRINT_COMMAND prints server-side and is empty, so disabled, by default; a rented server has no route to your printer, so print from your browser (our suggestion). Uploads are capped by HBOX_WEB_MAX_UPLOAD_SIZE in MB and stored under /data/GROUP-ID/documents/ (repo_item_attachments.go). S3 and other object stores are documented; keeping files local lets one backup cover them.

Backups, and putting one back

The database alone misses every photo, and copying a WAL database mid-write can tear it. The script takes a consistent copy with SQLite's .backup while Homebox runs, checks it, then archives the rest of /data plus .env and compose.yaml. Run sudo apt install -y sqlite3, save it as /root/homebox-backup.sh, run sudo bash /root/homebox-backup.sh.

set -euo pipefail
umask 077
b=/var/backups/homebox
t=$(date +%F-%H%M)
install -d -m 700 "$b"
sqlite3 /srv/homebox/data/homebox.db ".backup '$b/homebox-$t.db'"
[ "$(sqlite3 "$b/homebox-$t.db" 'PRAGMA integrity_check;')" = ok ]
tar -czf "$b/files-$t.tar.gz" -C /srv/homebox --exclude='data/homebox.db*' data .env compose.yaml
tar -tzf "$b/files-$t.tar.gz" > /dev/null
echo "backup $t ok"
if find "$b" -name "files-$t.tar.gz" -mmin -60 | grep -q .; then
  find "$b" -name 'homebox-*.db' -mtime +14 -delete
  find "$b" -name 'files-*.tar.gz' -mtime +14 -delete
fi

Sets older than 14 days are pruned only after a fresh archive exists. Nightly, in /etc/cron.d/homebox-backup: 20 3 * * * root bash /root/homebox-backup.sh > /var/log/homebox-backup.log 2>&1. Copy /var/backups/homebox off the machine with plain rsync -a, never --delete.

Live restore, as root (sudo -i), every step in that same shell so $b and $t stay set:

  1. b=/var/backups/homebox; t=YYYY-MM-DD-HHMM; [ "$(sqlite3 "$b/homebox-$t.db" 'PRAGMA integrity_check;')" = ok ] && tar -tzf "$b/files-$t.tar.gz" > /dev/null && echo verified. No verified, no restore: pick another set.
  2. [ ! -e /srv/homebox/data.previous ] && docker compose -f /srv/homebox/compose.yaml stop homebox && mv /srv/homebox/data /srv/homebox/data.previous && test -f /srv/homebox/data.previous/homebox.db && echo set aside. It must print set aside. If not, an earlier data.previous is still there: move it elsewhere or delete it, then rerun this step.
  3. `test ! -e /srv/homebox/data && test -d /srv/homebox/data.previous && tar -xzf "$b/files-$t.tar.gz" -C /srv/homebox data && cp "$b/homebox-$t.db" /srv/homebox/data/homebox.db && rm -f /srv/homebox/data/homebox.db-wal /srv/homebox/data/homebox.db-shm && chown -R 65532:65532 /srv/homebox/data && echo restored`. No stale -wal or -shm may sit beside the restored file.
  4. Only after restored: docker compose -f /srv/homebox/compose.yaml up -d, then check items with photos. Good? rm -r /srv/homebox/data.previous.
  5. Bad? Roll back, discarding any earlier data.failed: docker compose -f /srv/homebox/compose.yaml stop homebox && rm -rf /srv/homebox/data.failed && mv /srv/homebox/data /srv/homebox/data.failed && mv /srv/homebox/data.previous /srv/homebox/data && docker compose -f /srv/homebox/compose.yaml up -d.

Onto a fresh machine, as root: install Docker Engine (28+) plus sqlite3, run only the firewall lines of the setup script (sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw; ufw allow OpenSSH; ufw --force enable), copy both files of one set to /root, then run `b=/root; t=YYYY-MM-DD-HHMM; test ! -e /srv/homebox && [ "$(sqlite3 "$b/homebox-$t.db" 'PRAGMA integrity_check;')" = ok ] && install -d -m 750 /srv/homebox && tar -xzf "$b/files-$t.tar.gz" -C /srv/homebox && cp "$b/homebox-$t.db" /srv/homebox/data/homebox.db && chown -R 65532:65532 /srv/homebox/data && docker compose -f /srv/homebox/compose.yaml up -d`. The archive brings .env, so the pepper matches, and compose.yaml, with sign-up already off. Skip the setup script, whose .env guard would stop it, and continue with the nginx steps.

Updates and the licence

By default Homebox checks GitHub and reports the newest release in /api/v1/status. To upgrade: run the backup, read the notes of every skipped release (0.26.0 made the pepper mandatory), change the tag, then sudo docker compose -f /srv/homebox/compose.yaml pull && sudo docker compose -f /srv/homebox/compose.yaml up -d. The docs describe no downgrade, so our rollback is the restore above with the old tag.

Homebox is AGPL-3.0. Our general reading, not legal advice: pass copies on, modified or not, and you pass on the source under the same licence; if you run a modified version that people use over a network, the AGPL requires offering those users the modified source. Running the unchanged release at home triggers neither.

When another tool fits better

Homebox tracks what you own, where it is, and its warranty, purchase and maintenance records. Pantry stock and chores suit Grocy; company laptops and licences suit Snipe-IT. Large multi-user installs belong on PostgreSQL, which this setup does not cover.

Questions

Is hay-kot/homebox still maintained?

No, it is archived on GitHub, and sysadminsmedia/homebox continues it. Back up /data, then point your setup at ghcr.io/sysadminsmedia/homebox. Since 0.26.0 a release will not start without HBOX_AUTH_API_KEY_PEPPER of at least 32 bytes, and the rootless image wants UID 65532 to own the data folder.

What is the default Homebox login?

There is none. You register the first account yourself, and each registration without an invite gets its own group. Do it over an SSH tunnel, then set HBOX_OPTIONS_ALLOW_REGISTRATION to false before the site is public.

Can Homebox use PostgreSQL instead of SQLite?

Yes. The database page documents the HBOX_DATABASE_* variables and recommends PostgreSQL for larger installations. The backup script here covers SQLite only.

Why does the container refuse to start after an upgrade?

Check the pepper first. Since 0.26.0, HBOX_AUTH_API_KEY_PEPPER must hold a random value of at least 32 bytes. Generate one with openssl rand -base64 48, keep it in a mode 600 file and never change it.

A whole machine for your inventory

Every dedicated server line, with its processor, memory, port and cities. Choose one and configure it.

See the machines

Related on Server Room

Other use cases