VoucherVault on Ubuntu 24.04 on Azure User Guide
Overview
VoucherVault is an open source, self hosted web application for keeping vouchers, gift cards and loyalty cards in one place. For each item it records the balance, expiry date, codes and barcodes or QR codes, and lets you attach the original document or image; a dashboard summarises what you hold and what is about to expire, and a built in scheduler can remind you before a card lapses. The cloudimg image installs VoucherVault as a Django 5.2 application in a dedicated Python virtual environment, served by gunicorn and reverse proxied behind nginx with TLS, then locks it down for a marketplace appliance. gunicorn is bound to loopback 127.0.0.1:8000 and never exposed directly; nginx is the only way in, terminating TLS on :443 (with :80 redirecting to https) plus an unauthenticated /healthz endpoint for load balancer probes. The datastore is SQLite, so the appliance runs standalone with no external database. VoucherVault has its own built in authentication, so this image is secure by default: no shared or default credential ships, and on the first boot of every VM a fresh Django secret key is generated and the administrator account is created with a unique, randomly generated password written to a root only file. A small set of example cards ships so the dashboard, inventory and item views are populated out of the box. Backed by 24/7 cloudimg support.
What is included:
- VoucherVault 1.30.1 installed in a dedicated Python virtual environment and running as the
vouchervaultgunicorn systemd service - The full VoucherVault web application behind an nginx TLS reverse proxy on
:443, with gunicorn bound to loopback only and:80redirecting to https - Built in Django authentication with no default account, and a unique administrator password generated on first boot
- A per VM Django secret key generated on first boot, never baked into the image
- Host settings configured so sign in works on the public IP address or a custom domain, with no baked in hostname
- A loopback Redis plus Celery worker and beat scheduler powering the expiry reminder feature
- A small set of example voucher, gift card and loyalty card entries so the dashboard and inventory are populated
- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes vouchervault.service(gunicorn),vouchervault-celery.service,vouchervault-beat.service,redis-server.serviceandnginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; VoucherVault is light on resources. NSG inbound: allow 22/tcp from your management network, 443/tcp for the TLS web application, and 80/tcp so the http to https redirect and the health probe work. The image ships a self signed certificate so you can sign in immediately over https; for production, add your own domain and certificate (see Maintenance).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for VoucherVault by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22), HTTP (80) and HTTPS (443). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name vouchervault \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Open ports 443 and 80 to your network so you can reach the web application:
az vm open-port --resource-group <your-rg> --name vouchervault --port 443 --priority 900
az vm open-port --resource-group <your-rg> --name vouchervault --port 80 --priority 901
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active vouchervault.service vouchervault-celery.service vouchervault-beat.service redis-server.service nginx.service
All five report active. VoucherVault runs under gunicorn on the loopback address 127.0.0.1:8000; a loopback Redis backs the Celery worker and beat scheduler; nginx fronts the application with TLS. gunicorn and Redis are never bound to a public interface, so nginx is the only way in.

Step 5 - Retrieve your admin password
VoucherVault uses its own Django login. The username is admin and a unique password is generated on the first boot of your VM and written to a root only file:
sudo cat /root/vouchervault-credentials.txt
This file contains VOUCHERVAULT_USERNAME, VOUCHERVAULT_PASSWORD and the VOUCHERVAULT_URL to open in a browser. The file is mode 0600 root:root. No default account ships in the image, and the per VM Django secret key lives in /opt/vouchervault/vouchervault.env (mode 0640 root:vouchervault) and is generated on first boot, never baked into the image. Store the password somewhere safe and change it after first sign in (see Maintenance).

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm authentication is required
Because VoucherVault manages its own login, the application cannot be reached without signing in. An unauthenticated request to a protected page returns HTTP 302 and redirects to the sign in page, while the health endpoint stays open. The -k flag tells curl to accept the image's self signed certificate:
echo "health : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/healthz)"
echo "dashboard: $(curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1/en/dashboard)"
It prints health : 200 and dashboard : 302. Every voucher, gift card and loyalty view requires the per VM admin password; only the health endpoint is open. gunicorn is bound to loopback and nginx is the only way in.

Step 8 - Confirm the installed version and components
VoucherVault runs on Django in a dedicated virtual environment. Confirm the framework version and that the core components are in place:
/opt/vouchervault/venv/bin/python -c "import django; print('Django', django.get_version())"
cat /opt/vouchervault/app/package.json
sudo ls /opt/vouchervault/venv/bin/gunicorn /opt/vouchervault/app/database/db.sqlite3 /etc/systemd/system/vouchervault.service
It reports Django 5.2, the VoucherVault version, and lists the gunicorn binary, the SQLite database on the OS disk and the systemd unit. The image ships VoucherVault 1.30.1 with a small set of example cards so the inventory is non empty on first boot.

Step 9 - Sign in
Browse to https://<vm-public-ip>/. Your browser will warn about the self signed certificate the first time; accept it to continue (add your own domain and certificate for production, see Maintenance). VoucherVault shows its sign in page. Enter admin and the password from Step 5.

After signing in you land on the dashboard. It summarises your collection - the total value across your available items, a breakdown by type (gift cards, coupons, vouchers and loyalty cards), an item distribution chart and a status overview - all populated from the example cards that ship with the image.

Step 10 - Browse your inventory
Choose Inventory from the sidebar to see every card as a tile with its issuer, name, value, expiry date and type. The filters along the top let you narrow by status (available, expiring soon, used, expired) or by type, and the search box finds a card by name or issuer.

Step 11 - Open an item and scan to redeem
Click any card to open its detail view. It shows the issue and expiry dates, the value, a description, and a generated barcode or QR code under Scan to Redeem (tap to reveal the code, which stays blurred by default so it is not exposed on screen). From here you can edit, duplicate, share, mark the card as used, or delete it, and record part redemptions as transactions so the remaining balance stays accurate.

Step 12 - Add your own cards and remove the examples
The image ships a few example cards so the dashboard is not empty. When you are ready, add your own from Create New Item in the sidebar: pick the type (voucher, gift card, coupon or loyalty card), enter the name, issuer, value, currency, expiry date and the redeem code, and optionally attach a document or image. VoucherVault generates the barcode or QR code for you. To remove an example once you have your own, open it from the inventory and choose Delete.
Step 13 - Enable expiry reminders (optional)
The image ships a loopback Redis with a Celery worker and beat scheduler, and a daily "Periodic Expiry Check" task is pre-created and enabled, so the reminder infrastructure is already running. To actually receive reminders, add a notification channel: sign in, open the user menu and choose Notifications, then enter one or more Apprise URLs (for example an SMTP, Telegram, Discord or ntfy target) and save. VoucherVault then notifies you before your cards expire. You can review or change when reminders fire, and the expiry threshold, from the same area.
Maintenance
- Password: the administrator password is generated on first boot and stored in
/root/vouchervault-credentials.txt(mode0600 root:root). Change it from the web interface after signing in, or reset it from the command line:cd /opt/vouchervault/app && sudo runuser -u vouchervault -- env DJANGO_SETTINGS_MODULE=myproject.settings SECRET_KEY=$(grep '^SECRET_KEY=' /opt/vouchervault/vouchervault.env | cut -d= -f2-) DB_ENGINE=sqlite3 DOMAIN=localhost /opt/vouchervault/venv/bin/python manage.py changepassword admin. - Custom domain and TLS: the image ships a self signed certificate. For production, point a DNS name at the VM and install your own certificate (for example with certbot), then add your domain to
DOMAINin/opt/vouchervault/vouchervault.env(comma separated with the existing values) so it is accepted for sign in, andsudo systemctl restart vouchervault. nginx trustsX-Forwarded-Proto, so sign in works over TLS on any host without further changes. - Reminders: the Celery worker and beat scheduler run as
vouchervault-celery.serviceandvouchervault-beat.serviceagainst the loopback Redis; add an Apprise notification URL under Notifications in the app to receive them (see Step 13). - Loopback binding: gunicorn is bound to
127.0.0.1:8000and Redis to127.0.0.1:6379, so nginx is the only path in. Keep it that way - do not change the bind addresses to a public interface. - Database: the SQLite database and uploaded documents live under
/opt/vouchervault/app/database/(ownedvouchervault:vouchervault). Back it up by copying the directory while the service is stopped. - Upgrades: VoucherVault is installed from a pinned release into its virtual environment. To upgrade, replace the source under
/opt/vouchervault/app, reinstall requirements with/opt/vouchervault/venv/bin/pip, runmanage.py migrateandmanage.py collectstatic, thensudo systemctl restart vouchervault. - Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.