Applications Azure

VoucherVault on Ubuntu 24.04 on Azure User Guide

| Product: VoucherVault on Ubuntu 24.04 LTS on Azure

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 vouchervault gunicorn systemd service
  • The full VoucherVault web application behind an nginx TLS reverse proxy on :443, with gunicorn bound to loopback only and :80 redirecting 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 /healthz endpoint for Azure Load Balancer health probes
  • vouchervault.service (gunicorn), vouchervault-celery.service, vouchervault-beat.service, redis-server.service and nginx.service as 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.

The VoucherVault gunicorn, Celery worker, Celery beat, Redis and nginx services all reporting active, with gunicorn on loopback 127.0.0.1:8000 and Redis on 127.0.0.1:6379 while only nginx listens on ports 80 and 443

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).

The per VM credentials file showing the generated admin username, the VoucherVault URL and a masked password, its 0600 root only permissions, and the per VM secret key environment file which is never baked into the image

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.

The health endpoint returning 200 unauthenticated and a protected page returning 302 to the sign in page without a session, alongside the full per VM login round trip proof passing with a wrong password rejected and the correct per VM password reaching the dashboard

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.

The Django and VoucherVault versions, the count and names of the example voucher gift card and loyalty card entries seeded on first boot, and the enabled systemd units for gunicorn, Celery, beat, Redis and nginx

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.

The VoucherVault sign in page with the username and password fields

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.

The VoucherVault dashboard showing the total value of the collection, the count of gift cards vouchers coupons and loyalty cards, and an item distribution chart

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.

The VoucherVault inventory listing the example Amazon Gift Card, Restaurant Voucher and Coffee Loyalty Card as tiles with issuer, value, expiry date and type, plus status and type filters

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.

The VoucherVault item detail for the Restaurant Voucher showing the issue date, expiry date, value, description and a generated Scan to Redeem QR code, with edit, duplicate, share, mark used and delete actions

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 (mode 0600 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 DOMAIN in /opt/vouchervault/vouchervault.env (comma separated with the existing values) so it is accepted for sign in, and sudo systemctl restart vouchervault. nginx trusts X-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.service and vouchervault-beat.service against 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:8000 and Redis to 127.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/ (owned vouchervault: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, run manage.py migrate and manage.py collectstatic, then sudo 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.