Warracker
Overview
Warracker is a self-hosted warranty tracker: a single searchable record of everything you own that came with a warranty, what you paid for it, when the cover runs out, and where the paperwork is. This SecureAnyCloud image runs the free and open-source, AGPL-3.0 licensed Warracker 1.0.2 natively on Ubuntu 24.04 on Azure, deployed from the Azure Marketplace.
Each product becomes one entry with its purchase date and price, vendor, serial and model numbers, warranty length or an exact expiry date, notes and tags. Receipts, invoices, manuals and product photos are uploaded and stay attached to that entry for as long as you keep it. The dashboard sorts and filters by expiry so the things about to run out surface on their own, and a status view charts what is active, what is expiring soon and what has lapsed.
Warracker can also tell you before cover lapses rather than after. Point it at your own mail server for expiry email, or at Discord, Slack, Telegram, Teams, ntfy, Pushover or a generic webhook through Apprise. It supports multiple users with an administrator role, optional single sign-on through any OpenID Connect provider, optional integration with a Paperless-ngx document archive, light and dark themes, and a translated interface in more than a dozen languages.
nginx serves the interface on port 80 and reverse-proxies the API to a gunicorn worker on the loopback address. The worker is backed by PostgreSQL 16, which also listens only on loopback.
Warracker is an independent open-source project. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by, or sponsored by the Warracker project. It ships the free and open-source AGPL-3.0 licensed software, unmodified.
What you get:
- Warracker 1.0.2, the AGPL-3.0 licensed upstream release, pinned by tag and verified against a SHA-256 checksum and the release commit at build time
- A version check: the version the code declares is asserted against the pinned version at build time and again after deployment, so a mislabelled upstream tag fails the build rather than shipping
- nginx on port
80serving the interface and reverse-proxying/api/, installed as a host package so unattended security upgrades keep the internet-facing component patched - gunicorn bound to
127.0.0.1:5000and PostgreSQL 16 bound to loopback, so the only thing exposed to your network is the web interface - Exactly one administrator, seeded on first boot with a password unique to your VM, and public registration closed in the same step
- A Flask session signing key, a PostgreSQL application role password and a PostgreSQL privileged role password, all generated per VM on first boot and written to a root-only file
- Fail-closed guards: both the application service and nginx refuse to start if any secret is empty, unrotated, or a published upstream default
- SMTP, OIDC and Apprise credentials shipped empty, so no mail account is ever baked into the image
- Uploaded documents stored with mode
0750under a0750data directory, never served directly by nginx, and reachable only through an authenticated route - The complete corresponding source, its checksum, the AGPL licence text and a written offer at
/usr/share/warracker/source - An Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security upgrades enabled
- The Azure Linux Agent for cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA
Prerequisites
- An Azure subscription with permission to create virtual machines
- An SSH key pair for administrative access to the VM
- A network security group that allows inbound TCP
80from the networks you trust, and TCP22from your administrative network only - A
Standard_B2sVM (2 vCPU, 4 GiB RAM), which is sufficient: Warracker, gunicorn and PostgreSQL all run comfortably in that footprint
Deploy from the Azure Portal
- In the Azure Portal, search the Marketplace for Warracker on Ubuntu 24.04 LTS by SecureAnyCloud and select Create.
- Choose your subscription, resource group and region.
- Set the VM size to Standard_B2s or larger.
- Under Administrator account, select SSH public key and paste your public key. The administrative user is
azureuser. - Under Inbound port rules, allow HTTP (80) and SSH (22). Restrict both to your own address ranges wherever you can.
- On the Disks tab, use Standard SSD for the OS disk unless you need something else.
- Select Review + create, then Create.
First boot takes about a minute. The appliance generates its own secrets, applies the database schema, seeds the administrator and closes registration before the web interface accepts a single request.
Deploy from the Azure CLI
Create the resource group and network security group, then the VM. Replace the values in angle brackets with your own.
# Placeholders: substitute your own resource group, region and address range.
az group create --name my-resource-group --location eastus
az network nsg create --resource-group my-resource-group --name warracker-nsg
az network nsg rule create --resource-group my-resource-group --nsg-name warracker-nsg \
--name allow-ssh --priority 1000 --destination-port-ranges 22 \
--source-address-prefixes <your-mgmt-cidr> --access Allow --protocol Tcp
az network nsg rule create --resource-group my-resource-group --nsg-name warracker-nsg \
--name allow-http --priority 1010 --destination-port-ranges 80 \
--source-address-prefixes <your-mgmt-cidr> --access Allow --protocol Tcp
# Accept the image terms once per subscription, before the first deployment.
az vm image terms accept --publisher sac --offer warracker-ubuntu-24-04 --plan default
az vm create \
--resource-group my-resource-group \
--name warracker-01 \
--image sac1647283583153:warracker-ubuntu-24-04:default:latest \
--size Standard_B2s \
--storage-sku StandardSSD_LRS \
--admin-username azureuser \
--generate-ssh-keys \
--nsg warracker-nsg \
--public-ip-sku StandardConnect to your VM
Connect over SSH as the azureuser administrator.
ssh azureuser@<vm-ip>Confirm the appliance is running
Four services make up the appliance. warracker-firstboot is a one-shot unit that provisions this machine's own secrets. It stays active after it completes because it is a RemainAfterExit unit.
systemctl is-active postgresql warracker-firstboot warracker nginxExpected output:
active active active activeConfirm the version that is actually deployed, rather than trusting a label.
grep -o "currentVersion = '[^']*'" /var/www/warracker/version-checker.js | head -1Expected output:
currentVersion = '1.0.2'Check what is listening. Only :22 and :80 are reachable from outside the VM. The application server and the database are both bound to the loopback address.
ss -tlnH | awk '{print $4}' | sort -uExpected output:
0.0.0.0:22 0.0.0.0:80 127.0.0.1:5000 127.0.0.1:5432 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:80Confirm the interface answers.
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/Expected output:
HTTP 200Read your per-instance credentials
Every secret on this appliance is generated on this machine during first boot. Nothing is baked into the image, so no two deployments share a credential and SecureAnyCloud does not know yours. Print the credentials file.
sudo cat /root/warracker-credentials.txtThe file is readable only by root. Confirm its mode and owner.
stat -c '%a %U:%G' /root/warracker-credentials.txtExpected output:
600 root:rootThe file contains the sign-in details plus the database credentials:
- WARRACKER_URL: the address this VM resolved for itself at first boot
- WARRACKER_ADMIN_USERNAME: the administrator sign-in name, admin
- WARRACKER_ADMIN_EMAIL: the administrator email address, used for password reset if you configure mail
- WARRACKER_ADMIN_PASSWORD: the administrator password, unique to this VM
- WARRACKER_DB_USER / WARRACKER_DB_PASSWORD: the PostgreSQL role the application uses
- WARRACKER_DB_ADMIN_USER / WARRACKER_DB_ADMIN_PASSWORD: the privileged PostgreSQL role
- WARRACKER_SECRET_KEY: the key that signs every session token
Change the administrator password after your first sign-in, from the account menu in the top right of the interface.
Sign in
Open http://<vm-ip>/ in a browser. You are sent to the sign-in page, because this appliance has no anonymous access at all.
Sign in with the WARRACKER_ADMIN_USERNAME and WARRACKER_ADMIN_PASSWORD values from the credentials file.
Note the line under the sign-in button: new account registration is currently disabled. This is deliberate, and it is the most important difference between this image and a stock Warracker deployment. See Step 7.
Why nobody else can claim your instance
In Warracker's own behaviour, the first person to register becomes the administrator and the owner of the instance. It accepts that first registration on an empty database regardless of the registration setting. On a public address, a stock deployment is therefore a race between you and whoever finds the port first.
This image never enters that state. First boot seeds exactly one administrator with the password in your credentials file. It closes public registration in the same step, before the web interface accepts any request at all.
Verify it yourself. A registration attempt is refused:
curl -s -o /dev/null -w 'HTTP %{http_code}\n' -X POST \
-H 'Content-Type: application/json' \
-d '{"username":"passerby","email":"a@b.com","password":"Passw0rd1"}' \
http://127.0.0.1/api/auth/registerExpected output, a genuine refusal and not a redirect:
HTTP 403Unauthenticated API requests are refused too.
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/api/warrantiesExpected output:
HTTP 401The published upstream database password does not work on your instance.
PGPASSWORD=<set-at-first-boot> psql -w -h 127.0.0.1 -U warranty_user -d warranty_db \
-c 'SELECT 1' </dev/null >/dev/null 2>&1 \
&& echo 'ACCEPTED' || echo 'rejected'Always include -w in a probe like this. Without it, psql prompts for a password and reads it from standard input, which in a script is the rest of the script. The run then ends early and still exits 0, so a test that never ran reports success.
Expected output:
rejectedThe same protection is enforced at startup, not only at first boot. A guard runs before both the application service and nginx. It refuses to let either serve if the signing key or either database password is empty, is still the value the image ships, or is a value published in Warracker's own source.
sudo /usr/local/sbin/warracker-preflight.shExpected output:
warracker-preflight: OKTo add more users later, sign in as the administrator and open Settings to re-enable registration. Alternatively, create the accounts yourself from the admin area.
Record a warranty with its paperwork
Select Add New Warranty. The dialog walks you through product details, the warranty term, documents, tags and a summary.
- Product: name, an optional link to the product page, one or more serial numbers, model number and vendor
- Warranty: purchase date and price, then a duration in years, months and days, an exact expiry date, or a lifetime warranty
- Documents: the receipt or invoice, the manual, a product photo, and any other supporting file (PDF, PNG, JPG)
- Tags: free-form labels you can filter on later
Saved entries appear on the dashboard, colour-coded by how much cover is left and sorted so the ones about to expire come first. Each card carries links to its attached documents and a countdown of the days remaining on the cover.
The Status view charts the whole collection: how many warranties are active, how many are expiring soon, how many have lapsed, and when the remaining ones run out.
Your uploaded documents are private
Receipts, invoices, manuals and product photos are user data, and this image treats them as private. The upload directory is not world-readable.
stat -c '%a %U:%G %n' /var/lib/warracker /var/lib/warracker/uploadsExpected output:
750 warracker:warracker /var/lib/warracker 750 warracker:warracker /var/lib/warracker/uploadsnginx refuses to serve the upload paths directly, so a document can never be fetched by guessing its filename.
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/uploads/anything.pdfExpected output:
HTTP 403The only route to a stored document is /api/secure-file/. It requires a valid session and checks that the session owns the warranty the document belongs to. An unauthenticated request is refused.
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/api/secure-file/example.pdfExpected output:
HTTP 401Expiry email and push notifications
Warracker can send expiry reminders by email and push them to a long list of services. Both ship switched off with empty credentials, because SecureAnyCloud never bakes a mail account into an image.
To enable email, add your own mail server details to the environment file and restart the service. This command edits configuration, so run it only when you have real values to hand.
# Replace every placeholder with your own mail server details before running this.
sudo sed -i \
-e 's|^SMTP_HOST=.*|SMTP_HOST=smtp.<your-domain>|' \
-e 's|^SMTP_USERNAME=.*|SMTP_USERNAME=<your-email>|' \
-e 's|^SMTP_PASSWORD=.*|SMTP_PASSWORD=<set-at-first-boot>|' \
-e 's|^SMTP_FROM_ADDRESS=.*|SMTP_FROM_ADDRESS=<your-email>|' \
/etc/warracker/warracker.env
sudo systemctl restart warrackerFor push notifications, sign in as the administrator and open Settings to enable Apprise and add your notification URLs. Examples are discord://webhook_id/webhook_token, telegram://BotToken/ChatID and ntfy://topic.
You can also set them in the same environment file.
# Replace the webhook id and token with your own before running this.
sudo sed -i -e 's|^APPRISE_ENABLED=.*|APPRISE_ENABLED=true|' \
-e 's|^APPRISE_URLS=.*|APPRISE_URLS=discord://webhook_id/<your-token>|' \
/etc/warracker/warracker.env
sudo systemctl restart warrackerThe reminder schedule lives in the same file. APPRISE_EXPIRATION_DAYS defaults to 7 and 30 days before expiry, and APPRISE_NOTIFICATION_TIME defaults to 09:00.
Where your data lives
- /var/lib/warracker/uploads: uploaded receipts, invoices, manuals and product photos
- PostgreSQL database warranty_db: warranty records, users, tags, settings and audit log
- /etc/warracker/warracker.env: runtime configuration and this VM's secrets, mode 0640 root:warracker
- /root/warracker-credentials.txt: the first-boot credential summary, mode 0600 root:root
- /opt/warracker/app: the deployed backend, migrations and translation catalogues
- /var/www/warracker: the deployed frontend served by nginx
- /usr/share/warracker/source: the AGPL corresponding source, its checksum, the licence and the written offer
A complete backup is the database plus the upload directory. The following commands write a dump, so run them deliberately.
# Backs up the database and the uploaded documents to <backup-dir>.
sudo -u postgres pg_dump warranty_db | gzip > <backup-dir>/warranty_db.sql.gz
sudo tar czf <backup-dir>/warracker-uploads.tar.gz -C /var/lib/warracker uploadsThe complete source is on the machine
Warracker is licensed under the GNU Affero General Public License v3. Section 13 of that licence gives anyone who interacts with the software over a network the right to its corresponding source. That source ships on this image, unmodified, with a checksum you can verify at any time.
ls -1 /usr/share/warracker/source/Expected output:
LICENSE WRITTEN-OFFER.txt warracker-1.0.2-source.tar.gz warracker-1.0.2-source.tar.gz.sha256Verify it:
cd /usr/share/warracker/source && sha256sum -c warracker-1.0.2-source.tar.gz.sha256Expected output:
warracker-1.0.2-source.tar.gz: OKWRITTEN-OFFER.txt records the upstream project, the exact version and the exact git commit this image was built from, and SecureAnyCloud's standing offer to supply the corresponding source to anyone who asks. The deployed tree itself, under /opt/warracker/app and /var/www/warracker, is interpreted Python and JavaScript, so it is source too.
Day to day operation
Restart the application after a configuration change.
sudo systemctl restart warrackerRead the application log.
sudo journalctl -u warracker -n 30 --no-pagerCheck that the database is healthy.
sudo -u postgres psql -w -d warranty_db -c 'SELECT COUNT(*) AS warranties FROM warranties;' </dev/nullSecurity updates are applied automatically. Confirm that unattended upgrades are enabled.
cat /etc/apt/apt.conf.d/20auto-upgradesExpected output:
APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1";Security hardening
- Restrict inbound ports. Limit
80and22in the network security group to the address ranges you actually use. Warracker holds purchase prices and scanned receipts. - Change the administrator password after the first sign-in, from the account menu.
- Put HTTPS in front of it. The appliance serves plain HTTP on port
80so that it works the moment it boots. For anything beyond a private network, terminate TLS with a certificate for a real hostname. Use an Azure Application Gateway or Front Door in front of the VM, or use certbot on the VM itself once a DNS name points at it.
To use certbot, first make sure a DNS name already points at this VM. Then run sudo apt-get install -y certbot python3-certbot-nginx, followed by sudo certbot --nginx -d <your-domain>.
After enabling HTTPS, update FRONTEND_URL and APP_BASE_URL in /etc/warracker/warracker.env so that links in reminder email point at the right address.
- Keep registration closed unless you are actively adding users, and prefer creating accounts yourself from the admin area.
- Back up regularly. Back up the database and the upload directory together, as described in Step 11.
- Leave the secrets alone. The signing key and both database passwords are generated for this VM. If you rotate them by hand, rotate them in
/etc/warracker/warracker.envand in PostgreSQL together, then restart. The startup guard refuses to serve on an inconsistent or empty value rather than fall back to a published default.
Troubleshooting
The web interface returns 502. gunicorn is not running. Check systemctl is-active warracker and journalctl -u warracker -n 50 --no-pager. If the startup guard rejected the configuration, the log says so explicitly.
The web interface returns nothing and nginx is inactive. Both nginx and the application are held until first boot has finished writing this VM's secrets. Check systemctl status warracker-firstboot. If it failed, journalctl -u warracker-firstboot gives the reason, and sudo systemctl restart warracker-firstboot safely re-runs it.
Sign-in fails. Confirm that you are using the values from /root/warracker-credentials.txt and not a password from another instance. A wrong password returns 401.
Registration is refused and you want more users. This is by design, as Step 7 explains. Re-enable registration in Settings, add the users, then switch it off again.
A document will not download. Documents are served only to a session that owns the warranty. Confirm that you are signed in as the right user. Then check journalctl -u warracker for the SECURE_FILE log lines, which record exactly which path was requested.
Time or expiry dates look wrong. Set the instance timezone from Settings, and confirm the VM clock with timedatectl.
Support
SecureAnyCloud provides 24/7 support with a guaranteed 24-hour response SLA for this image, covering deployment, configuration and operation of the appliance.
- Email: sales@secureanycloud.com
- Guides: https://secureanycloud.com/guides/
Warracker itself is an independent open-source project. Send product feature requests and upstream bug reports to https://github.com/sassanix/Warracker.
Deploy on Azure
Launch Warracker on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com