InvenTree 1.7.0 on AWS User Guide
Overview
This guide covers launching and using the InvenTree 1.7.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg help desk, IT service and project management listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
InvenTree is an open source inventory management system for teams that build or stock physical products. It keeps a parts catalogue with categories, parameters and suppliers, tracks stock across locations with a full movement history, holds bills of materials for assemblies, and runs purchase, sales and build orders against that stock. Everything in the web interface is also available through a documented REST API, and plugins extend it.
What the cloudimg image adds:
- Nothing baked into the image. The image ships with no database, no administrator and no secret keys. First boot creates an empty PostgreSQL database with its own password, a new Django secret key, a new OpenID Connect signing key and a certificate for the instance, then creates the administrator
adminwith a password unique to your instance. - No default logins. The package's install step that creates an administrator and writes its password to a file is switched off, so the only account is the one first boot creates for you. The image's self test (Step 3) confirms that common passwords such as
admin,inventree,passwordand123456are refused. - HTTPS from the first boot. nginx serves InvenTree on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, and a request for a host name the instance does not serve gets no answer.
- The application server is not on the network. InvenTree's package starts its gunicorn application server on
0.0.0.0:6000, which would put Django on the network without TLS. On this image gunicorn listens on127.0.0.1:6000only, PostgreSQL listens on the loopback address only, and the only open ports are 22, 80 and 443. - The configured host name, never the caller's. nginx passes InvenTree the instance's configured name, so links InvenTree builds always point at your instance whatever
HostorX-Forwarded-*headers a client sends, and InvenTree's sign in rate limit sees the real client address. - Uploaded files need a signed in user. Pictures and attachments under
/media/are sent only to a signed in user, as downloads, however the path is spelled. - Metadata access blocked. nginx, gunicorn, InvenTree's background worker, PostgreSQL, first boot and the
inventree-managehelper are blocked from the instance metadata service, so an address a user enters (a supplier link, an image URL, a webhook) can never reach it, and the AMI requires IMDSv2. - No system pip. InvenTree runs from its own Python environment in
/opt/inventree/env, with its own current pip. Ubuntu'spython3-pip,python3-wheelandpython3.12-venvpackages, whose security fixes are published only through Ubuntu Pro, are removed, and a small package namedcloudimg-inventree-python-depsstands in for them soaptstays consistent. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: InvenTree 1.7.0 (the project's own Ubuntu 24.04 package from the 1.7.0 GitHub release, checksum verified), Django 5.2, gunicorn, PostgreSQL 16, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH) from your admin network, and TCP 443 (InvenTree) and TCP 80 (redirects to HTTPS) from the networks your users work from. Your parts, stock and supplier data are in InvenTree, so keep 80 and 443 as narrow as you can.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick InvenTree 1.7.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger for many users or large imports), your key pair and a security group as above.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=inventree}]'
First boot builds the database and takes two to three minutes after the instance is running. The web server starts only when it has finished.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| InvenTree 1.7.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
PostgreSQL, the InvenTree web server (inventree-web-1), its background worker (inventree-worker-1) and nginx are running, and first boot has finished:
systemctl is-active postgresql@16-main inventree-web-1 inventree-worker-1 nginx
sudo test -f /var/lib/cloudimg/inventree-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on the network. gunicorn and PostgreSQL listen on the loopback address:
sudo ss -lntp | awk 'NR>1 && $4 !~ /^127\.0\.0\.5[34]/ {split($6, p, "\""); print $4, p[2]}' | sort
0.0.0.0:22 sshd
0.0.0.0:443 nginx
0.0.0.0:80 nginx
127.0.0.1:5432 postgres
127.0.0.1:6000 gunicorn
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
The image's self test checks the open ports, signs in with your password, confirms that common passwords are refused, and checks the HTTPS redirect. It takes about 30 seconds:
sudo inventree-selftest.sh
[ports] off-box TCP exactly [22 80 443]; gunicorn 127.0.0.1:6000; PostgreSQL loopback
[ports] INVENTREE_PORTS_OK
[login] per-instance credential authenticates (API token 200, browser sign-in 200 with a working session); default/weak passwords refused
[login] INVENTREE_LOGIN_OK
[http] port 80 redirects to https://<configured host>/
INVENTREE_SELFTEST_OK
Step 4: Get your URL and password
sudo cat /root/inventree-credentials.txt
The file is readable by root only:
# InvenTree 1.7.0 on Ubuntu 24.04 by cloudimg
# Generated on this instance's first boot. These credentials are unique to THIS instance.
INVENTREE_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
INVENTREE_ADMIN_USER=admin
INVENTREE_ADMIN_PASSWORD=<INVENTREE_ADMIN_PASSWORD>
# Sign in at the URL above as 'admin'. The certificate is a self-signed certificate made for this instance on its first
# boot: compare its SHA-256 fingerprint (sudo openssl x509 -in /etc/ssl/inventree/inventree.crt -noout -fingerprint
# -sha256) or replace it with a CA-issued certificate (see the user guide). Change the served name with
# sudo inventree-set-hostname <name>.
The URL uses the instance's public DNS name from the EC2 instance metadata, and the certificate also names the public and private IP addresses. nginx answers only to those names. The name is fixed at first boot, so if you stop and start the instance without an Elastic IP and its public address changes, run sudo inventree-set-hostname with the new name (Step 8), or better, give the instance an Elastic IP or your own DNS name.
Step 5: Sign in
Open the INVENTREE_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time; compare the fingerprint with the command in the credentials file, or install your own certificate (Step 8). Sign in as admin with the password from Step 4:

admin is a superuser, and InvenTree shows a Superuser Mode banner as a reminder. For everyday work, create a user for each person under Admin Center, Users / Access and give them the groups and roles they need, and keep admin for administration.
To change the admin password, open the user menu (top right), select User Settings, then Change Password from the menu beside Account Details. Then delete or update INVENTREE_ADMIN_PASSWORD in /root/inventree-credentials.txt so it does not hold an old password. If you lose it, another superuser can set a new one under Admin Center, Users / Access; the Security panel of User Settings also holds multi-factor authentication and your access tokens.
Step 6: Parts, stock and bills of materials
The screenshots below show a small example data set (an electronics assembly and its parts) that was entered during the image build to show the product working; your instance starts with an empty database.
Parts. Parts holds your catalogue, organised in categories. Each part has an internal part number (IPN), units, parameters, suppliers and prices, and can be marked as an assembly, a component, purchaseable, salable or trackable:

Bills of materials. An assembly part has a Bill of Materials tab listing each component and the quantity one assembly uses. InvenTree shows the stock available for each line and how many assemblies you can build from it; Manufacturing turns that into build orders that consume the stock:

Stock. Stock shows every stock item, its quantity, status and location. Locations nest (a warehouse, its shelves, its bins), and every receipt, move, count and consumption is recorded in the item's history:

Purchasing and sales. Purchasing holds suppliers, manufacturers, supplier parts and purchase orders; receiving a purchase order books the stock in. Sales holds customers, sales orders and shipments.
Step 7: Administration, e-mail and the background worker
Admin Center. The user menu's Admin Center holds users and groups, background tasks, error reports, data import and export, project codes and the plugin settings. Its System Status panel shows InvenTree's own checks; on a new instance the only warning is that e-mail is not configured yet:

System settings. System Settings (in the user menu) holds the company name, currencies, label and report templates, barcode support, and how parts, stock and orders behave.
E-mail. Password reset and notification e-mails need an SMTP server (Amazon SES works as the relay, port 587 with TLS). Put InvenTree's e-mail settings in a file of their own in /etc/inventree/conf.d, which the services read at start up (inventree-set-hostname rewrites config.yaml and conf.d/other, but leaves other files alone), then restart the web server and worker:
sudo install -m 0640 -o inventree -g inventree /dev/null /etc/inventree/conf.d/email
sudo nano /etc/inventree/conf.d/email
# export INVENTREE_EMAIL_HOST=<smtp-host>
# export INVENTREE_EMAIL_PORT=587
# export INVENTREE_EMAIL_TLS=True
# export INVENTREE_EMAIL_USERNAME=<smtp-user>
# export INVENTREE_EMAIL_PASSWORD=<smtp-password>
# export INVENTREE_EMAIL_SENDER=<inventree@your-domain>
sudo systemctl restart inventree-web-1 inventree-worker-1
The background worker. InvenTree's background tasks (e-mail, notifications, report generation, stock and pricing updates, clean up) run in inventree-worker-1 with django-q2, using PostgreSQL as its queue, so the image needs no Redis. Admin Center, Background Tasks shows the queue and any failed tasks.
The Django console. inventree-manage runs InvenTree's manage.py as the inventree user, in a short lived systemd unit with the same metadata block as the services. Use it for maintenance commands such as Django's system check:
sudo inventree-manage check 2>&1 | tail -1
System check identified no issues (0 silenced).
The REST API. Every page of the web interface uses InvenTree's REST API at /api/, and so can your own scripts. The API root answers without a sign in and reports the version:
curl -sk https://127.0.0.1/api/ | python3 -c 'import json, sys; d = json.load(sys.stdin); print(d["server"], d["version"], "API", d["apiVersion"])'
InvenTree 1.7.0 API 570
Everything else needs a user. Create an API token for a user under User Settings, Security, Access Tokens (or GET /api/user/me/token/ with the user's name and password), and send it as Authorization: Token <token>; a token has the rights of the user it belongs to.
Step 8: Your own DNS name and certificate
Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name InvenTree is served as. This mints a new self signed certificate for the name, points nginx and InvenTree's allowed hosts, trusted origins and site URL at it, updates the URL in /root/inventree-credentials.txt, and keeps the name across reboots:
sudo inventree-set-hostname <your-domain>
To use a certificate from a public or internal certificate authority instead (for example one issued by Let's Encrypt for <your-domain>), copy it over the generated pair and reload nginx (running inventree-set-hostname again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/ssl/inventree/inventree.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/inventree/inventree.key
sudo nginx -t && sudo systemctl reload nginx
Step 9: Security checks
nginx serves only the application. Without a sign in, the API answers 401 and uploaded files under /media/ are refused, hidden files answer 404, plain HTTP redirects to HTTPS, and a request for a name the instance does not serve gets no answer at all:
H=$(sudo grep '^INVENTREE_URL=' /root/inventree-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
for p in /web/ /api/ /api/part/ /media/ /.git/config /.env; do
curl -sk -o /dev/null -w "%{http_code} $p\n" --resolve "$H:443:127.0.0.1" "https://$H$p"
done
curl -s -o /dev/null -w 'http:// -> %{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'unknown host name -> %{http_code}\n' --resolve "other.example:443:127.0.0.1" "https://other.example/" || true
200 /web/
200 /api/
401 /api/part/
401 /media/
404 /.git/config
404 /.env
http:// -> 301
unknown host name -> 000
PostgreSQL listens on the loopback address only, and InvenTree runs with Django's debug mode off and an explicit list of allowed hosts:
sudo -u postgres psql -X -tA -c "SHOW listen_addresses"
sudo grep -E '^debug:' /etc/inventree/config.yaml
localhost
debug: False
The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^INVENTREE_URL=' /root/inventree-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost
None of the services can reach the instance metadata service, so an address a user enters can never reach it:
systemctl show nginx inventree-web-1 inventree-worker-1 postgresql@16-main inventree-firstboot -p Id -p IPAddressDeny
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=nginx.service
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=inventree-web-1.service
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=inventree-worker-1.service
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=postgresql@16-main.service
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
Id=inventree-firstboot.service
sudo inventree-manage <command> runs Django's commands with the same block.
Step 10: Backup, storage and maintenance
Everything you enter is in the PostgreSQL database inventree (/var/lib/postgresql/16/main); uploaded pictures and attachments are under /opt/inventree/data/media, and the configuration, the Django secret key and the OpenID Connect key are in /etc/inventree. All of it is on the root volume, so the simplest backup is an EBS snapshot of it. For a consistent copy while InvenTree is running:
sudo -u postgres pg_dump -Fc inventree > <backup-dir>/inventree-$(date +%F).dump
sudo tar -C / -czf <backup-dir>/inventree-files-$(date +%F).tar.gz opt/inventree/data/media etc/inventree
The dump and /etc/inventree hold every password hash and key, so store them as carefully as the instance. InvenTree also has its own backup command (sudo inventree-manage dbbackup), which writes to /opt/inventree/data/backup.
To give the database and attachments more room, enlarge the root volume in the EC2 console (Volumes, Modify); Ubuntu grows the file system on the next boot.
New InvenTree releases arrive as new versions of this listing; support can help you move your data to one. Operating system security updates install automatically; run sudo apt-get update && sudo apt-get upgrade to apply everything else. nginx, PostgreSQL and InvenTree restart cleanly and keep all of their data.
| Component | Path |
|---|---|
| InvenTree | /opt/inventree (code in src, Python environment in env) |
| Configuration and keys | /etc/inventree (config.yaml, secret_key.txt, oidc.pem) |
| Database | PostgreSQL 16 database inventree (/var/lib/postgresql/16/main) |
| Uploaded files, static files, backups | /opt/inventree/data/media, static, backup |
| nginx site | /etc/nginx/sites-available/inventree |
| Certificate and key | /etc/ssl/inventree/inventree.crt, /etc/ssl/inventree/inventree.key |
| Services | inventree-web-1, inventree-worker-1, postgresql@16-main, nginx |
| Helpers | /usr/local/sbin/inventree-set-hostname, inventree-manage, inventree-selftest.sh |
| Credentials | /root/inventree-credentials.txt (mode 0600) |
Step 11: Security recommendations
- Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your users work from.
- Use your own DNS name and a certificate your users' browsers trust (Step 8).
- Give each person their own InvenTree user with the narrowest roles that fit, and keep
adminfor administration (Step 5). - Use API tokens rather than passwords for scripts, one token per script, and revoke tokens you no longer use.
- Install plugins only from sources you trust: a plugin runs inside InvenTree with full access to its data.
- Keep the operating system patched, and snapshot the root volume regularly.
Support and licensing
InvenTree is open source under the MIT licence, by the InvenTree developers; there is no per user or per part licence fee. The image ships InvenTree exactly as the project packages it for Ubuntu 24.04, with two changes to the package's start up files: gunicorn listens on the loopback address instead of every address, and the package's own administrator creation is switched off. The AMI price covers cloudimg's hardening, first boot setup and support.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.