What Homebox does for you, and which version to use
Homebox answers the questions every household asks: where did we put it, when did we buy it, is it still under warranty. You file items under locations with photos, receipts, manuals, purchase and maintenance notes. For food and chores, Grocy fits better; for a company's equipment, Snipe-IT.
Many guides still point at hay-kot/homebox, but GitHub shows it archived, untouched since June 2024. The maintained continuation is sysadminsmedia/homebox (docs at homebox.software), published as ghcr.io/sysadminsmedia/homebox for 64-bit Intel/AMD and 64-bit ARM, in regular, rootless and hardened flavours. Its quick-start makes the move from the old image sound like a name swap. Since 0.26.0 you also need HBOX_AUTH_API_KEY_PEPPER (32 bytes or longer), and the rootless image expects the data folder to belong to user 65532 (install page), or it won't start.
The server that fits a home inventory
Homebox barely notices the hardware: its README says the container idles below 50 MB of memory, and no processor or disk minimum is published. Our reading: the photo folder is what grows, so think about disk before cores.
- A small cartridge server from our dedicated server range suits one job like this. Prefer the m510 or m710x, which include a console for when a firewall slip locks you out; the rest can only be switched on and off remotely.
- A single-processor rack server makes sense if Homebox will share the box with other self-hosted apps; every rack server includes its console.
- ARM works through the 64-bit ARM image: the made-to-order Ampere Altra on our ARM servers page. Leave the X-Gene cartridge aside, as it runs only Ubuntu 16.04 and 18.04, far too old for this guide. No 32-bit ARM image exists.
A ready server reaches you within 30 minutes of approval; a custom build takes longer. Whichever you pick, nobody else shares the hardware, you log in as root, nobody meters the port, and there is no contract.
Up and running in an evening
Install Docker Engine with its Compose plugin from Docker's Ubuntu guide. Copy the script below into /root/homebox-install.sh, then start it with sudo bash /root/homebox-install.sh. It turns the firewall on with SSH kept open, insists on Docker 28 or newer, refuses to run twice, writes a root-only secrets file and starts Homebox.
set -euo pipefail
sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
ufw allow OpenSSH
ufw --force enable
ver=$(docker version --format '{{.Server.Version}}')
if [ "${ver%%.*}" -lt 28 ]; then echo "Docker $ver is too old: install 28+"; exit 1; fi
h=/opt/homebox
if [ -e "$h/.env" ]; then echo "Already set up: $h/.env is there, stopping"; exit 1; fi
mkdir -p "$h"
chmod 750 "$h"
mkdir -p "$h/data"
chown -R 65532:65532 "$h/data"
pep=$(openssl rand -base64 48)
if [ "${#pep}" -lt 32 ]; then echo "openssl gave no pepper"; exit 1; fi
install -m 600 /dev/null "$h/.env"
{ echo "HBOX_AUTH_API_KEY_PEPPER=$pep"; echo "HBOX_OPTIONS_ALLOW_REGISTRATION=true"; } > "$h/.env"
cat > "$h/compose.yaml" <<'EOF'
services:
homebox:
image: ghcr.io/sysadminsmedia/homebox:0.27.1-rootless
container_name: homebox
restart: always
env_file: /opt/homebox/.env
environment:
- HBOX_OPTIONS_TRUST_PROXY=true
- HBOX_OPTIONS_ALLOW_ANALYTICS=false
volumes:
- /opt/homebox/data:/data
ports:
- "127.0.0.1:7745:7745"
EOF
docker compose -f "$h/compose.yaml" up -d
Why 127.0.0.1: before the port? With no address, Docker listens on 0.0.0.0 and [::] and skips past ufw for IPv4 traffic, plus IPv6 traffic if you have switched on IPv6 networking inside Docker. With the prefix, only the server itself connects, a guarantee Docker gives from version 28, so the script checks the version.
The pepper is a secret mixed into stored API keys, mandatory since 0.26.0. The configuration docs say Homebox will not start with under 32 bytes, and a new pepper breaks every issued API key, so the script never overwrites it. Turning trust-proxy on now is our choice; nothing outside can reach the port yet.
Make the first account yours, then lock the door
Homebox ships without a preset password: whoever registers first gets the first account, and service_user.go gives every uninvited sign-up a household of its own. An open sign-up page lets anyone store things on your server.
- From your own laptop, open a tunnel through your normal SSH login:
ssh -L 7745:127.0.0.1:7745 you@your-server. - Browse to http://localhost:7745 and create your account with a strong password.
- Back on the server, close sign-up and restart:
sudo sed -i '/^HBOX_OPTIONS_ALLOW_REGISTRATION=/c HBOX_OPTIONS_ALLOW_REGISTRATION=false' /opt/homebox/.env && sudo grep -qx 'HBOX_OPTIONS_ALLOW_REGISTRATION=false' /opt/homebox/.env && sudo docker compose -f /opt/homebox/compose.yaml up -d --force-recreate - Invite your family from your profile with Generate Invite Link. Invited sign-ups still work after registration is closed, per v1_ctrl_user.go.
Put it on your own web address
Caddy fetches and renews the HTTPS certificate itself, and Homebox's proxy page has a Caddy example. Ubuntu packages Caddy from 24.04 on, not in 22.04. Create an A record for your domain pointing to the server, plus an AAAA record if it has IPv6. Then let the status endpoint (controller.go) confirm sign-up is closed before anything goes public: if curl -s http://127.0.0.1:7745/api/v1/status | grep -F '"allowRegistration":false'; then sudo apt install -y caddy; else echo 'sign-up still open: fix that first'; fi
Next, replace the contents of /etc/caddy/Caddyfile with this, using your own domain:
inventory.example.com {
reverse_proxy 127.0.0.1:7745 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote_host}
header_up X-Real-IP {remote_host}
}
}
Open ports 80 and 443 (sudo ufw allow proto tcp from any to any port 80,443), then run sudo systemctl reload caddy. Caddy runs outside Docker, so those rules are what admit visitors and the certificate check on IPv4 and IPv6. This is the docs' second Caddy example; we changed the address to your local port and dropped encode gzip (optional compression) and header_upstream Host {host} (Caddy 2 passes Host through unchanged).
Printing labels and scanning QR codes
Its tips page lists a QR code for every item and location, a QR scanner for your phone, label sheets by asset ID on the Tools page for boxes and bins, and a single label from an item's Labels button for a label printer. Asset IDs count up by themselves (000-001, 000-002); typing #000-001 into search jumps to that item.
The codes carry your Homebox address, one reason the proxy docs insist on HBOX_OPTIONS_TRUST_PROXY. Server-side printing through HBOX_LABEL_MAKER_PRINT_COMMAND is off by default, and a data-centre server cannot see the printer in your hallway, so print from your browser. Uploads are capped at 10 MB by default (HBOX_WEB_MAX_UPLOAD_SIZE).
Keeping your inventory safe
Database and photos must travel together, or a restore brings back items without pictures. The script below stops Homebox while it archives, so the copy is consistent, packs the data folder with .env and compose.yaml, checks the archive reads back, and restarts Homebox even if a step failed. Save it as /root/homebox-save.sh, then start it with sudo bash /root/homebox-save.sh.
set -euo pipefail
umask 077
dest=/var/backups/homebox
stamp=$(date +%F-%H%M)
cf=/opt/homebox/compose.yaml
install -d -m 700 "$dest"
docker compose -f "$cf" stop homebox
trap 'docker compose -f "$cf" start homebox' EXIT
tar -czf "$dest/homebox-$stamp.tar.gz" -C /opt/homebox data .env compose.yaml
tar -tzf "$dest/homebox-$stamp.tar.gz" > /dev/null
echo "saved $dest/homebox-$stamp.tar.gz"
if find "$dest" -name "homebox-$stamp.tar.gz" -mmin -60 | grep -q .; then
find "$dest" -name 'homebox-*.tar.gz' -mtime +30 -delete
fi
Archives over 30 days old go only once a fresh one exists. Nightly runs need one line in /etc/cron.d/homebox-save: 40 2 * * * root bash /root/homebox-save.sh > /var/log/homebox-save.log 2>&1. Copy /var/backups/homebox elsewhere with rsync -a, without --delete. You manage the server; we keep the hardware and the network running.
To bring an archive back onto the live server, as root (sudo -i):
- Pick it and test it:
a=/var/backups/homebox/homebox-YYYY-MM-DD-HHMM.tar.gz; rm -rf /root/hb-test && mkdir /root/hb-test && tar -xzf "$a" -C /root/hb-test && sqlite3 /root/hb-test/data/homebox.db 'PRAGMA integrity_check;'(installsqlite3first). Go on only if it prints ok. - Move today's data out of the way without deleting it:
test ! -e /opt/homebox/data.before && docker compose -f /opt/homebox/compose.yaml stop homebox && mv /opt/homebox/data /opt/homebox/data.before && test -f /opt/homebox/data.before/homebox.db && echo moved aside. You should see moved aside. If you don't, an older data.before is in the way: move it somewhere else or delete it, then repeat this step. - Unpack the archive into the now-empty spot:
test ! -e /opt/homebox/data && test -d /opt/homebox/data.before && tar -xzf "$a" -C /opt/homebox data && chown -R 65532:65532 /opt/homebox/data && echo unpacked. Because the folder is fresh, no leftover homebox.db-wal or -shm file from the live database can be replayed into the restored one; never drop a lone homebox.db over a folder that still has them. - Once you see unpacked, bring Homebox up again (
docker compose -f /opt/homebox/compose.yaml up -d) and open a few items with photos. Happy?rm -r /opt/homebox/data.before /root/hb-test. - Unhappy? Swap back:
test ! -e /opt/homebox/data.bad && docker compose -f /opt/homebox/compose.yaml stop homebox && mv /opt/homebox/data /opt/homebox/data.bad && mv /opt/homebox/data.before /opt/homebox/data && docker compose -f /opt/homebox/compose.yaml start homebox. If an old data.bad is there, delete or move it first, and remove data.bad once you've checked.
Moving to a new server works from the same archive, as root there:
- Put Docker on it (version 28 or later), then switch the firewall on by running lines 2 to 4 of the install script, and nothing more: the IPV6 sed line, the OpenSSH rule and the forced enable, in that order. Don't run the whole script: its .env check would stop it, by design.
- Copy the archive across, test it as in step 1 above, and set
a=to its path. mkdir -p /opt/homebox && chmod 750 /opt/homebox && tar -xzf "$a" -C /opt/homebox && chown -R 65532:65532 /opt/homebox/data && docker compose -f /opt/homebox/compose.yaml up -d. Your pepper, settings and image version arrive with it, sign-up still closed.- Carry on with the Caddy steps above for your web address.
Staying current, and what the licence asks
Homebox checks GitHub for releases unless told not to, and its status endpoint names the latest. They matter: 0.27.0 fixed 15 security issues. Back up, read the notes, edit the version in compose.yaml, fetch the release (sudo docker compose -f /opt/homebox/compose.yaml pull) and recreate the container (sudo docker compose -f /opt/homebox/compose.yaml up -d). The docs give no downgrade path, so our way back is the pre-update archive with the old version.
Homebox is licensed under the AGPL-3.0. This is our general reading and not legal advice: hand copies to others, changed or unchanged, and you must make the source available under the same licence; run a modified Homebox that other people reach over the internet, and those users can claim the modified source. Using the official release at home asks nothing of you.
Questions people ask
- Can my partner and kids share one inventory?
- Yes. Send them an invite link from your profile. People who join through it see your items and locations; anyone signing up without one gets a separate, empty inventory. Invites work after public registration is closed.
- Do I have to use Docker?
- No, there are ready-made programs for Linux, macOS and Windows too. The install page still recommends Docker for production, which is why this guide uses it.
- Where do my photos and receipts end up?
- In the same data folder as the database, under a directory per household, unless you point Homebox at S3 or another object store. That is why the backup here archives the whole folder rather than the database file alone.
- What if I lose the pepper?
- Homebox needs a pepper to start, so you would set a new one, and every API key created before then would stop working. The configuration page ties the pepper to stored API key hashes and names no other effect. Keeping .env inside every backup avoids the problem.