What you will set up
Snipe-IT keeps a register of what your company owns and who has it: laptops and phones with their serial numbers and asset tags, software licences and their seats, headsets and other accessories, and consumables such as toner. You check things out to people and back in again, and every move is logged. A user can be asked to accept what they receive and sign for it on screen, and Snipe-IT prints labels with QR codes, reminds you of audits and warranties, and imports from spreadsheets.
Snipe-IT is made by Grokability, Inc., a company in California, and developed in the open on GitHub. It is open source under the AGPL-3.0 licence, and the version you run yourself has every feature, including LDAP, SAML and the API. Grokability also sells it hosted, from 39.99 US dollars a month or 399.99 a year when we checked on 14 October 2026: they run the server, upgrades and maintenance, and add email support. The larger plans add IP restrictions, a private server and a VPN. In our tests the server made no connections to the outside, as step 20 describes.
Here Snipe-IT runs from its official image, snipe/snipe-it, as a pod under a user of its own called snipeit, with MariaDB for its data, behind Caddy from the Podman guide.
Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:
- Snipe-IT 8.8.0 started with MariaDB 12.3. The setup wizard and the first admin were done over an SSH tunnel, before Caddy was set up, and the wizard was closed afterwards.
- Login Attempts showed each visitor's real address, over IPv4 and IPv6, and ignored a forged
X-Forwarded-Forheader. - A company, two offices, three departments and four people were created, with categories, manufacturers, models, a custom fieldset with MAC address and RAM, and assets with the tags
EX-00001onwards. - A laptop was checked out to a user, who got an email, signed for it in the browser and got the signed terms as a PDF. It was checked in, with an email, and out again over the API.
- Without any mail settings, checking out a laptop failed with "Server Error", which the mail line in step 3 prevents. The emails in this guide went to a test SMTP server on the same server.
- A licence got five seats, two of them assigned. A headset was handed out, toner was used, and the low stock alert arrived by email.
- A sheet of A4 labels came out as a PDF, and its QR codes opened the right assets.
- Three laptops were imported from a CSV file, one of them checked out to its user. An audit set the next one twelve months ahead, and the reminder arrived by email.
- A teammate in a read-only group could see everything and change nothing. The interface was shown in Swedish, for one user and as the default.
- Snipe-IT's built-in backup worked. Our own backup was restored onto a new instance, under a new user with empty volumes, with logins, the API token and the signed PDF intact.
- An update from 8.7.2 to 8.8.0 ran its database migrations by itself, and everything came back by itself after a reboot.
The pod used about 260 MB of memory, about half of it MariaDB.
Before you start
You need:
- a server set up as in the Podman guide, with Caddy running;
- an A record and an AAAA record for
assets.example.compointing at your server; - an email account to send from, for step 17.
The examples use assets.example.com for Snipe-IT, 203.0.113.10 for your server's IPv4 address, which ip -brief address show eth0 shows, and Example AB as the company. Replace them throughout.
1. Create the user
As root:
useradd -m -s /bin/bash snipeit
loginctl enable-linger snipeit
machinectl shell snipeit@
Everything up to step 5 runs as snipeit.
2. Create the secrets
podman run --rm docker.io/snipe/snipe-it:v8.8.0 php artisan key:generate --show | tr -d '\n' | podman secret create snipeit-app-key -
openssl rand -hex 24 | tr -d '\n' | podman secret create snipeit-db-password -
The first line downloads the Snipe-IT image, about 1 GB, and uses it to make the application key, APP_KEY. Snipe-IT encrypts sessions, custom fields you mark as encrypted, and the LDAP password with it. Without it a restored backup is of little use, so step 21 saves a copy. The second line makes the database password. Both are stored by Podman, not in the files below.
3. Define the pod
mkdir -p ~/.config/containers/systemd
cd ~/.config/containers/systemd
Create snipeit.pod:
[Pod]
PodName=snipeit
# Only Caddy, on this server, can reach Snipe-IT: the port is not open to the internet.
PublishPort=127.0.0.1:8196:80
[Install]
WantedBy=default.target
Create snipeit-db.container:
[Container]
ContainerName=snipeit-db
Image=docker.io/library/mariadb:12.3
Pod=snipeit.pod
Volume=snipeit-db:/var/lib/mysql
Environment=MARIADB_DATABASE=snipeit MARIADB_USER=snipeit MARIADB_RANDOM_ROOT_PASSWORD=1 MARIADB_AUTO_UPGRADE=1
Secret=snipeit-db-password,type=env,target=MARIADB_PASSWORD
HealthCmd=healthcheck.sh --connect --innodb_initialized
HealthInterval=10s
Notify=healthy
AutoUpdate=registry
[Service]
Restart=always
And snipeit-app.container:
[Unit]
Requires=snipeit-db.service
After=snipeit-db.service
[Container]
ContainerName=snipeit-app
Image=docker.io/snipe/snipe-it:v8.8.0
Pod=snipeit.pod
Volume=snipeit-data:/var/lib/snipeit
# Step 5 changes this to the public address.
Environment=APP_URL=http://localhost:8196
Environment=APP_TRUSTED_PROXIES=203.0.113.10 APP_TRUSTED_HEADERS=HEADER_X_FORWARDED_FOR,HEADER_X_FORWARDED_PROTO
Environment=APP_TIMEZONE=Europe/Stockholm APP_LOCALE=en-US
# Step 17 replaces this line.
Environment=MAIL_MAILER=log
Environment=DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=snipeit DB_USERNAME=snipeit
Secret=snipeit-app-key,type=env,target=APP_KEY
Secret=snipeit-db-password,type=env,target=DB_PASSWORD
[Service]
Restart=always
What the settings do:
APP_URLis the address Snipe-IT puts in every link, form, email and QR code. It starts as the address of the SSH tunnel in step 4, and becomeshttps://assets.example.comin step 5.APP_TRUSTED_PROXIESis the address Snipe-IT trusts to tell it the visitor's real address. Caddy's connections arrive in the pod from the server's own IPv4 address, as the Podman guide explains, so that is the one to name. Since Snipe-IT 8.8,APP_TRUSTED_HEADERSmust also name the headers to trust; without it, every login shows203.0.113.10.APP_TIMEZONEgives Swedish time in the interface and in the scheduled tasks of step 18.MAIL_MAILER=logmakes Snipe-IT drop its emails until step 17 sets up sending. Without any mail settings, the image's defaults make every checkout that sends an email fail with "Server Error".DB_CONNECTION=mysqltalks to MariaDB the same way asmariadbdoes. The image has only MySQL's client tools, and withmariadbthe built-in backup of step 21 fails with "Command not found".- MariaDB stays on 12.3, the long-term release that our MariaDB guide uses, and
MARIADB_AUTO_UPGRADEupdates its system tables after an update within it. Snipe-IT keeps a fixed version; step 22 updates it. - Uploaded files, signed terms, the API keys and the built-in backups go in the
snipeit-datavolume; the register itself is in the database.
Start it:
systemctl --user daemon-reload
systemctl --user start snipeit-pod
systemctl --user enable --now podman-auto-update.timer
On the first start Snipe-IT creates its database tables, which takes about a minute. It is ready when this prints 302:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8196/
4. Run the setup wizard over an SSH tunnel
Until the first admin exists, anyone who opens Snipe-IT gets a setup page that creates one, so do this before Snipe-IT can be reached from the internet. From your own computer, open an SSH tunnel to the server:
ssh -L 8196:127.0.0.1:8196 root@203.0.113.10
Open http://localhost:8196 in your browser. That goes straight to Snipe-IT, not through Caddy.
- Pre-Flight checks the setup. Every line should be green, except Config File, which says it cannot check whether the
.envfile is exposed. It is not, and step 5 checks that. Choose Next: Create database tables. - This step says
Nothing to migrate., as the tables already exist, and creates the keys for the API. Choose Next: Create an Admin User. - Fill in a Site Name, such as
Example AB IT, your name, Email, Username and a Password of at least 10 characters. Untick Email my credentials to the email address above, since email comes in step 17. Under the asset tag settings, enter a Prefix, such asEX-, keep the length at5and tick Generate auto-incrementing asset tags, so new assets getEX-00001,EX-00002and so on. Leave Full Multiple Companies Support off for one company, set Default Currency toSEK, and choose Next: Save User and Finish.
Snipe-IT logs you in and shows the dashboard. Close the tunnel with exit.
5. Put Caddy in front
Back on the server, as snipeit, change the address to the public one and turn on secure cookies, which only travel over HTTPS:
sed -i 's|APP_URL=http://localhost:8196|APP_URL=https://assets.example.com SECURE_COOKIES=true|' ~/.config/containers/systemd/snipeit-app.container
systemctl --user daemon-reload
systemctl --user restart snipeit-app
Go back to root with exit, switch to machinectl shell caddy@, and add this block at the end of ~/Caddyfile:
assets.example.com {
reverse_proxy 127.0.0.1:8196
}
Restart Caddy with systemctl --user restart caddy, and open https://assets.example.com. Log in with the username and password from step 4. Then check, from any computer, that the wizard is closed and the configuration file is not served:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' https://assets.example.com/setup
curl -s -o /dev/null -w '%{http_code}\n' https://assets.example.com/.env
The first should print 302 and the start page, the second 403.
6. First settings
The gear at the top right opens Admin Settings. Go through these first:
- Localization: the Default Language for everyone who has not picked one, a 24-hour clock under Time and Date Display, Monday under Day the Week Starts On, and SEK.
- General Settings: under EULA & Acceptance Preferences, write your terms of use in Default EULA, such as who is responsible for the equipment and what happens when someone leaves, and tick Require Signature if users should sign on screen when they accept something.
- Notifications: Send alerts to, such as
it@example.com, and the thresholds for stock, expiring warranties and licences, and audits, which step 15 uses. - Security: Enable Two Factor, to require or allow a code from an authenticator app at login.
To use Snipe-IT in Swedish, choose Swedish under Localization > Default Language for everyone. Each user can also pick a language under Edit Profile in the menu with their name at the top right.
7. Company, locations, departments and people
The Settings section of the left menu holds the lists everything else refers to. Create, with the orange + above each list:
- Companies: your company, such as
Example AB. - Locations: each office, such as
Stockholm officeandGothenburg office, with city and country. - Departments: such as
IT,SalesandFinance, each with its company and location.
Then add people with Create New > User at the top: First Name, Last Name, Username, Email, a Password, and their Company, Department and Location. This user can login lets them log in to see their own things (step 11). Until email works, leave Send welcome email to new users off, and give them the password yourself.
Snipe-IT can also import users from Active Directory or another LDAP server, under Admin Settings > LDAP, and let them log in with single sign-on, such as authentik, under SAML or Google. We did not test those here. If the sign-in service runs on this same server, Snipe-IT needs one of the fixes in step 8 of the Podman guide to reach it; with the second, APP_TRUSTED_PROXIES must name 10.0.2.0 instead.
8. Categories, models and custom fields
Every asset has a model, and every model has a category and a manufacturer. Under Settings in the left menu:
- Categories: create one per kind of thing, with its Type:
LaptopsandPhonesas Asset,Softwareas License,Headsetsas Accessory andToneras Consumable. For laptops, tick Use the primary default EULA instead., Require users to confirm acceptance of assets in this category. and Send email to user upon checkin/checkout. - Manufacturers: such as
Lenovo,Apple,Microsoft,JabraandHP. - Custom Fields: fields that only some assets have. The page has two lists. Under Custom Fields, create
MAC Address, with the FormatMAC, andRAM (GB), withNUMERIC. Under Fieldsets, create one calledLaptop, open it and add both fields. - Asset Models: such as
ThinkPad T14 Gen 5in Laptops by Lenovo, with the FieldsetLaptopand an EOL of 48 months, andiPhone 16in Phones.
9. Add assets
Choose Create New > Asset. The Asset Tag is filled in, such as EX-00001. Pick the Model, a Status such as Ready to Deploy, the Serial and the Default Location. The laptop's fieldset adds MAC Address and RAM (GB). Under Optional Information and Order Related Information, enter the Purchase Date, Purchase Cost and Warranty in months. Save.
The asset's page shows everything about it, with its history, when its warranty ends and when it reaches end of life, and a QR code. Lookup by Asset Tag at the top finds an asset by its tag.
10. Check out and check in
The buttons at the top right of the asset's page show their names when you point at them. Choose Checkout, pick the User, add Notes if you like, and choose Checkout. With the laptop category from step 8, the page lists what will happen: the user gets an email with the terms and a link to accept the laptop, and has to sign. If the user is with you, tick Sign/Accept in place to let them accept on your screen.
In our test, Erik got the email Asset checked out: EX-00001, followed the link, ticked I accept 1 item, signed with the mouse and chose Accept this item. He then got an email with the signed terms as a PDF, which is also stored under his EULAs tab. Reports > Unaccepted Items lists what has not been accepted yet.
To take the laptop back, choose Checkin on its page, pick a Status, add a note such as Screen scratched, and choose Checkin. The user gets an email, and the asset's History tab shows both moves.
11. What users see
People without any rights in a group only see their own things. After logging in, Erik saw View Assigned Items, with tabs for his assets, licences, accessories, consumables and signed EULAs, and a banner when something waited for his acceptance, which Accept Assets in his menu also lists. The asset list, other users and the settings gave him 403. Edit Profile lets users change their language, and their name if Allow users to edit their own profiles is on under General Settings.
12. Licences, accessories and consumables
- Licenses, with Create New > License at the top: a name such as
Microsoft 365 Business Standard, the number of Seats, the product key, and the Expiration Date. Checkout on the licence assigns a free seat to a user or a computer; the licence page shows each seat and who has it. - Accessories, with Create New > Accessory: things you hand out without tracking each one, such as
Jabra Evolve2 65, with a quantity and a Min. QTY. Checkout gives one to a user, and Checkin takes it back. - Consumables, with Create New > Consumable: things that are used up, such as toner, with a quantity and a Min. QTY. Checking one out to a user lowers the stock for good.
When the stock of an accessory or consumable falls below its minimum, Snipe-IT emails a low inventory report to the Send alerts to address (step 18).
13. Labels and QR codes
Under Admin Settings > Labels, tick New Label Engine and Save. The page then lists label templates for A4 and US sheets and for Brother, Dymo and Zebra label printers. Pick yours, such as Sheets\Avery\L7163_A, an A4 sheet of 14 labels, choose under Label visible fields what each label shows, such as asset tag, model and serial number, and Save.
To print, go to Assets, tick the assets, choose Generate Labels in the list of bulk actions, and Go. Snipe-IT makes a PDF to print at 100 % size. Each label has a QR code that opens the asset's page in Snipe-IT, and a barcode of the asset tag. In our test, each QR code read back as the address of its asset.
14. Import from a spreadsheet
To bring in assets you already keep in a spreadsheet, save it as CSV with column names Snipe-IT recognises, such as:
Asset Tag,Model Name,Category,Manufacturer,Serial,Status,Location,Purchase Date,Purchase Cost,Username,MAC Address,RAM (GB)
EX-00101,ThinkPad T14 Gen 5,Laptops,Lenovo,PF4ABD11,Ready to Deploy,Gothenburg office,2026-06-10,14990,,8c:16:45:aa:cc:11,32
EX-00103,MacBook Air 13 M4,Laptops,Apple,C02ZK1ABCD,Ready to Deploy,Stockholm office,2026-04-02,13995,sara,,16
Choose Import in the left menu, Select File... and the file. Then choose Import next to it, Assets as the CSV import type, and Next. Snipe-IT matches the columns to its fields, custom fields included; check the matches, choose Preview, and then Process import. Backup before importing? makes a built-in backup first.
Models, manufacturers and locations that do not exist yet are created, and a Username checks the asset out to that user. Asset tags that already exist are skipped, unless you tick Update Existing Values?.
15. Audits
An audit records that someone has seen an asset where it should be. Under Admin Settings > Notifications, set the Audit Interval, such as 12 months, and the Audit Warning Threshold, such as 30 days.
To audit an asset, choose Audit on its page, or Assets > Due for Audit, add a note, and choose Audit. Snipe-IT records the Last Audit and sets the Next Audit Date twelve months ahead. Scanner Bulk Audit in the same menu takes one asset tag after another, from a barcode scanner. Every night, Snipe-IT emails a list of the assets due for audit within the threshold.
16. A read-only teammate
Rights are given to groups. Under Admin Settings > Groups, choose the orange +, name the group, such as IT read-only, and grant, with the green tick, Reports Access and the View permission under Assets, Licenses, Accessories, Consumables, Components and Users, and nothing else. Add your teammate under Add Users to Group, or under Groups when you edit the user, and Save.
In our test, Lars in this group could open the asset list, each asset and the licences, while editing, checking in, creating and Admin Settings gave him 403. Give Admin Access to people who should manage the register but not the settings, and Super User only to those who run the server.
17. Email
Snipe-IT sends email for checkouts, acceptances and checkins, welcome mails and password resets, and the alerts of step 18. With MAIL_MAILER=log from step 3, it drops them all:
- checkouts and checkins work, but users only learn of them when they log in, where a banner asks them to accept;
- I forgot my password on the login page says a recovery email has been sent, and nothing arrives;
- no alerts are sent;
- Admin Settings > General Settings > Test Mail > Send Test shows "Mail could not be sent."
To send email, point Snipe-IT at your email provider's SMTP server, or the one from the mail server guide. As snipeit, store the SMTP password as a secret:
printf %s 'the-smtp-password' | podman secret create snipeit-mail-password -
Then, in snipeit-app.container, replace the comment and the line Environment=MAIL_MAILER=log with these lines, using your own server, account and addresses:
Environment=MAIL_MAILER=smtp MAIL_HOST=mail.example.com MAIL_PORT=587 MAIL_ENCRYPTION=tls MAIL_TLS_VERIFY_PEER=true
Environment=MAIL_USERNAME=assets@example.com MAIL_FROM_ADDR=assets@example.com MAIL_REPLYTO_ADDR=it@example.com
Environment="MAIL_FROM_NAME=Example AB IT" "MAIL_REPLYTO_NAME=Example AB IT"
Secret=snipeit-mail-password,type=env,target=MAIL_PASSWORD
Restart, and use Send Test to check it:
systemctl --user daemon-reload
systemctl --user restart snipeit-app
Port 587 with MAIL_ENCRYPTION=tls uses STARTTLS; for port 465, which uses TLS from the start, change the port. Keep MAIL_TLS_VERIFY_PEER=true: without it, the image does not check the mail server's certificate. We checked that against a test SMTP server on our own server with a self-signed certificate on port 587: with the line, Snipe-IT refused it with certificate verify failed, and without it, sent the mail anyway. All the emails in this guide went to that test server, so none left it; we did not send through a real provider.
18. Alerts and scheduled tasks
The image runs Laravel's scheduler once a minute, so there is no cron job to set up. To see what it runs, as snipeit:
podman exec -u docker snipeit-app php artisan schedule:list
Every night at midnight, Swedish time, Snipe-IT checks low stock, warranties and licences that expire within the Expiring Alerts Threshold, assets due for checkin and assets due for audit, and emails what it finds to Send alerts to. Every Sunday at midnight it makes a built-in backup (step 21). To run a check now, such as the audits:
podman exec -u docker snipeit-app php artisan snipeit:upcoming-audits
In our test, the low inventory report and the audit reminder arrived this way, and the container's log showed the scheduler running its tasks.
19. The API
Snipe-IT has a REST API for scripts and other systems. In the menu with your name, choose Manage API keys > Create New, give the key a name, and choose Create New. The token is shown once; keep it in your password manager. It has the same rights as your account, so for a script, make a user with only the rights it needs.
To look up an asset by its tag, with your token in place of TOKEN:
curl -s -H 'Authorization: Bearer TOKEN' -H 'Accept: application/json' \
https://assets.example.com/api/v1/hardware/bytag/EX-00001
To check out the asset with ID 1 to the user with ID 2:
curl -s -X POST -H 'Authorization: Bearer TOKEN' -H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"checkout_to_type": "user", "assigned_user": 2, "note": "Back after repair"}' \
https://assets.example.com/api/v1/hardware/1/checkout
The answer starts with {"status":"success". Snipe-IT's API reference lists the rest.
20. Outside services
We captured the server's DNS lookups and outgoing connections while Snipe-IT started, while assets were checked out and in, labels made, a CSV imported and a backup taken. There were none: Snipe-IT does not check for updates or send usage data.
In the browser, Edit Profile shows a small picture from secure.gravatar.com, run by Automattic in the United States, for users without a picture of their own. It sends a hash of the Gravatar Email field, which is empty unless the user fills it in, and, like any web request, the user's IP address. Avatars in lists use Snipe-IT's own default picture.
21. Back up
Snipe-IT has a backup of its own, under Admin Settings > Backups > Generate Backup, which also runs every Sunday. It makes a ZIP file with the database, the uploaded files, the signed terms and the API keys, which you can download from that page and restore with its Restore button. It has two limits: the ZIP stays in the snipeit-data volume on the same server, and it does not include the application key. We made one and checked its contents, but did not restore it.
Back up yourself as well. As snipeit:
umask 077
mkdir -p ~/backup
podman exec snipeit-db sh -c 'mariadb-dump -usnipeit -p"$MARIADB_PASSWORD" --single-transaction snipeit' > ~/backup/snipeit-db.sql
podman volume export snipeit-data > ~/backup/snipeit-data.tar
podman secret inspect --showsecret --format '{{.SecretData}}' snipeit-app-key > ~/backup/app-key.txt
That is the database, the volume with every file, and the application key. The dump runs while Snipe-IT keeps working. Copy ~/backup to another machine, for example with restic, and store it safely: it holds your whole register and the key.
To restore on a new server, do steps 1 to 3 there, with three changes: skip the first line of step 2, as the key comes from the backup, write snipeit-app.container with the public APP_URL and SECURE_COOKIES=true from step 5 already in place, and do not start the pod. Then, with the backup in ~/backup:
tr -d '\n' < ~/backup/app-key.txt | podman secret create snipeit-app-key -
systemctl --user daemon-reload
podman volume create snipeit-data
podman volume import snipeit-data ~/backup/snipeit-data.tar
systemctl --user start snipeit-db
podman exec -i snipeit-db sh -c 'mariadb -usnipeit -p"$MARIADB_PASSWORD" snipeit' < ~/backup/snipeit-db.sql
systemctl --user start snipeit-app
Starting only the database first loads the backup before Snipe-IT sets up an empty one. Add Caddy as in step 5, and the secret and lines from step 17 if you use email. To restore over an existing installation instead, first run systemctl --user stop snipeit-pod and podman volume rm snipeit-db snipeit-data, and skip the secret.
We tested this by deleting the user snipeit with everything it had, and restoring into a new one. Everyone could log in with their old password, the API token still worked, and Erik's signed PDF was there.
22. Update
podman auto-update keeps MariaDB 12.3 up to date. Snipe-IT keeps the version in its file, as its documentation recommends, so you choose when to update it. Read the release notes on GitHub, take a backup as in step 21, and change the version, such as to v8.8.1:
sed -i 's|snipe-it:v8.8.0|snipe-it:v8.8.1|' ~/.config/containers/systemd/snipeit-app.container
systemctl --user daemon-reload
systemctl --user restart snipeit-app
podman image prune -af
Snipe-IT updates its database when it starts, and journalctl --user -u snipeit-app lists each migration it ran. Our update from 8.7.2 to 8.8.0 took under a minute, download included, and the assets, users and settings were all there afterwards. The last line removes the old image.
Troubleshooting
Every login in Login Attempts has the server's own address. APP_TRUSTED_HEADERS is missing, or APP_TRUSTED_PROXIES names the wrong address. It must be the server's IPv4 address, not 127.0.0.1. Fix it in snipeit-app.container, then daemon-reload and restart snipeit-app.
The browser goes to https://assets.example.com during the setup wizard. APP_URL is already the public address. Set it to http://localhost:8196 as in step 3, then daemon-reload and restart snipeit-app.
Checking something out gives "Server Error". The app has no mail settings at all. Add Environment=MAIL_MAILER=log as in step 3, or set up email as in step 17.
"Mail could not be sent." Snipe-IT writes the reason to its own log, inside the container:
podman exec snipeit-app tail -n 20 storage/logs/laravel.log
certificate verify failed means the mail server's certificate does not match MAIL_HOST; use the host name the provider gives.
The built-in backup fails with "Command not found". DB_CONNECTION is mariadb. Set it to mysql as in step 3.