Checkout:
- assets.AssignUser + POST /app/assets/{id}/checkout assign/unassign an
asset's user independent of the full edit form. New shared
#checkoutModal, wired from a "Check Out..." context-menu action, backed
by GET /app/assets/{id} and GET /app/users/options JSON endpoints.
Quantity / uniqueness:
- Item types gain an IsUnique flag (checked by default, matching prior
behavior). Assets gain a Quantity column, fixed at 1 for unique types
and adjustable via +/- steppers (POST /app/assets/{id}/quantity) for
non-unique ones. Flipping a type back to unique clamps existing assets'
quantity back to 1. CSV import/export gained a Quantity column.
Migration 0003_quantity.sql.
Contacts:
- New models.RoleContact: an assignable person who cannot sign in
(User.CanLogin). Migration 0004_contacts.sql rebuilds the users table
(SQLite can't alter a CHECK constraint in place) to allow the role.
Users form/list updated; contact creation auto-generates an unusable
password since the login path is blocked by role regardless.
QR asset tags and printable labels:
- Each asset's QR code (via github.com/skip2/go-qrcode, pure Go) encodes
an absolute /scan/<tag> URL; GET /scan/{tag} resolves it and redirects
into /inventory?asset=<id>, which app.js picks up on load to
auto-select that asset - works from a phone camera or a USB
scanner-as-keyboard-wedge pointed at the address bar.
- GET /labels renders a print-ready sheet (right-click an asset for one
label, a folder for every asset in it) with a template switcher between
Avery 5160 (verified dimensions) and 61526 asset tag labels (best-effort
layout, flagged as such on the page).
Also refreshed README to reflect the right-click-first tree UX and all of
the above.
inventree
A small, self-contained IT asset inventory system. The heart of inventree is a nested folder tree — like a file manager — for organizing physical and virtual IT assets (servers, switches, laptops, printers, etc.).
- Single compiled Go binary, embedded HTML templates and static assets
- SQLite storage (WAL mode, foreign keys, migrations)
- Bootstrap 5 UI, minimal vanilla JavaScript (no Node/React/build step)
- Session-based auth with two roles: Admin and User
- Custom, admin-defined asset types with custom fields
- CSV import/export, full-text-ish search with breadcrumbs
Quick start
go build -o inventree ./cmd/inventree
./inventree serve
Then open http://localhost:8080 and log in with:
Username: admin
Password: admin
(This default account is created intentionally on first run. Change or disable it via the Users admin page once you're up and running.)
The database file is created automatically at data/inventree.db on first
run, with migrations applied and seed data (default statuses, item types,
and the admin account) inserted.
CLI
inventree # same as `inventree serve`
inventree serve # start the web server
inventree version # print the version
inventree database migrate # apply pending migrations and exit
inventree database backup [path] # write a consistent VACUUM INTO snapshot
serve flags:
| Flag | Default | Description |
|---|---|---|
-addr |
:8080 |
Listen address |
-db |
data/inventree.db |
Path to the SQLite database file |
-secure-cookies |
false |
Mark the session cookie Secure (enable when serving over HTTPS) |
/healthz is available for load balancer / container health checks.
The inventory tree
The Inventory page is the default page after login and the center of the application: a folder/asset tree on the left, and a detail panel on the right that shows whatever is selected (a folder's contents, or a single asset).
- Folders can nest arbitrarily deep, and contain both subfolders and assets.
- Expand/collapse state is remembered per user (persisted server-side), and folder contents are fetched lazily via small AJAX requests — the page never fully reloads when you browse the tree.
- Drag and drop folders and assets onto another folder (or onto "Inventory" itself) to move them. Admin only.
- Right-click for everything: it's meant to feel like a native file manager. Right-click a folder, an asset, a breadcrumb crumb, or empty tree/panel background for the actions available there (New Subfolder, Add Asset, Rename, Check Out, Move, Print Label, Delete, …) instead of hunting for a toolbar button. The main panel itself stays blank until you actually click into a folder or asset — browsing happens in the tree.
- Deleting a folder that still contains subfolders or assets requires confirmation, and offers a safe "move contents elsewhere" option instead of an accidental cascade. A "permanently delete everything inside" option is also available for when that's genuinely what you want.
- Search (top of the tree pane) looks across asset ID, name, folder name, item type, assigned user, notes, and custom field values, and shows each result's full folder breadcrumb.
Item types and custom fields
Admins can define arbitrary asset types (Laptop, Switch, Firewall, …) under Item Types, each with its own set of custom fields (text, long text, number, date, boolean, dropdown, URL, IP address), marked required or not, with a default value and display order. A representative starter set of types and fields is seeded on first run — feel free to edit or delete it.
Checkout, quantity, and contacts
- Check out / check in: right-click an asset → Check Out… to assign it to a user (or clear the assignment to check it back in). Works from any asset row, not just its detail view.
- Quantity vs. unique: item types have a Unique flag (checked by default). Unique types — a laptop, a server — get one asset row per physical unit, same as before. Uncheck it for bulk/consumable types (a USB cable, a box of screws) and assets of that type are tracked as a single row with a Quantity and +/- steppers in the asset panel and folder tables, instead of one row per unit. Flipping a type back to Unique clamps any existing quantities back down to 1.
- Contacts: under Settings → Users, a third role alongside Admin and User. Contacts can't sign in at all — they exist purely so you can assign assets to end users (e.g. "this laptop belongs to Jane in Accounting") without creating them a login account. A blank password is fine when creating one.
QR asset tags and printable labels
Every asset can have a QR-coded label printed for it: right-click an asset
for Print Label, or right-click a folder for Print Labels… (every
asset in that folder, recursively). The QR code encodes an absolute
/scan/<Asset ID> URL, so scanning it with a phone camera — or a USB
barcode/QR scanner focused on the browser's address bar — opens straight to
that asset.
The print page supports two label sheet layouts, switchable from a dropdown on the page itself:
- Avery 5160 (1" x 2-5/8", 30/sheet) — standard address labels, exact published dimensions.
- 61526 asset tag labels (3/4" x 2", ~48/sheet) — the grid/margins here are a best-effort layout, not a verified spec sheet, so print one test page on plain paper and check it against a real label before committing a full (often non-refundable) sheet.
CSV import / export
- Export: from the tree's right-click menu, or
/export/assets.csv(optionally scoped to a folder, with?recursive=1to include subfolders). Includes aQuantitycolumn and onecf_<field_key>column per distinct custom field in use. - Folder-scoped import: right-click a folder → Import CSV… imports
every row directly into that folder. No
Foldercolumn needed — that's the whole point of doing it from a specific folder. Never available at the top level. - Global import: Settings → Import / Export has the full-featured
version — a CSV with a
Foldercolumn (a/-separated path, e.g.Main Office/Network) that can optionally auto-create missing folders — plus a separate CSV import for bulk-adding/updating Statuses (Name,Color). Both matching-by-name/Asset-ID semantics and full column references with examples are documented right on that page.
A full backup/restore of everything (folders, assets, types, custom fields,
statuses, users) is just inventree database backup — it's a consistent
snapshot of the whole SQLite database.
Project layout
cmd/inventree/ CLI entrypoint (serve, version, database migrate/backup)
internal/
auth/ session management, secure cookies
database/ SQLite connection, pragmas, migrations
migrations/ embedded .sql migration files
models/ shared domain types
folders/ nested folder tree
assets/ asset CRUD, search, CSV import/export
itemtypes/ admin-defined types + custom field definitions
users/ accounts
statuses/ configurable asset statuses
settings/ key/value app settings
web/ HTTP router, handlers, template rendering
web/
templates/ Go html/template pages and partials (embedded)
static/ Bootstrap 5, Bootstrap Icons, app.css, app.js (embedded)
deploy/ systemd unit example
Development
go build ./...
go vet ./...
go test ./...
The SQLite driver is modernc.org/sqlite,
a pure-Go implementation, so CGO_ENABLED=0 builds work out of the box and
the compiled binary has no C library dependencies.
Docker
docker compose up --build
This builds the image, starts inventree on port 8080, and persists the
database in a named volume (inventree-data, mounted at /app/data).
Or directly:
docker build -t inventree .
docker run -p 8080:8080 -v inventree-data:/app/data inventree
systemd
See deploy/inventree.service for an example unit. Adjust the paths, then:
sudo useradd --system --home /opt/inventree --shell /usr/sbin/nologin inventree
sudo mkdir -p /opt/inventree/data
sudo cp inventree /opt/inventree/inventree
sudo chown -R inventree:inventree /opt/inventree
sudo cp deploy/inventree.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now inventree
Scope
inventree is intentionally a focused MVP: folders, assets, custom types, statuses, users, search, and CSV import/export. It does not include ticketing, purchasing, monitoring, RMM, MDM, or accounting features — if you need those, look elsewhere and integrate via CSV.