Deployment guide

PG Back Web

Overview

PG Back Web gives PostgreSQL backups a user interface. Instead of hand-written pg_dump scripts scattered across crontabs, you register each database once and describe when it should be backed up and where the dump should go. You then watch every run succeed or fail from a single dashboard, with retention, one-click restores and webhook notifications built in.

This guide covers the deployment and operation of PG Back Web on Ubuntu 24.04 on Azure, using the SecureAnyCloud Azure Marketplace image. The image runs PG Back Web v0.5.1, built reproducibly from source at build time from the exact upstream commit that the release tag points at. /opt/pgbackweb/VERSION records both the release tag and the source commit, and the running server reports the same version on its About page.

The first boot generates everything sensitive on this appliance for your machine alone. PG Back Web stores the connection string of every database it backs up, and the access key and secret key of every object store destination, inside its own PostgreSQL database. A key held in PBW_ENCRYPTION_KEY encrypts those values. If the image contained that key, every deployment would share it, and anyone holding a copy of the image could decrypt the stored credentials of every other deployment. The image therefore never contains the key. Your instance generates 32 random bytes on first boot, and the service refuses to start if the key is empty, is still the SecureAnyCloud build-time value, or matches a value published in the upstream documentation.

The administrator account follows the same rule. PG Back Web ships with no seeded account. A stock deployment presents a "Create first user" page to anyone who reaches it, so on an internet-facing server the first stranger to arrive becomes the administrator. This image closes that window. First boot creates the administrator with a password unique to your VM before port 80 is ever opened, and the reverse proxy then closes the account creation route because it has no further legitimate use.

What you get:

  • PG Back Web v0.5.1, built from source at a pinned upstream commit and running under systemd as the unprivileged pgbackweb system user
  • PostgreSQL 18 as the PG Back Web metadata store, bound to loopback only with scram-sha-256 authentication and no trust method anywhere in its configuration
  • The pg_dump and psql client tools for every supported PostgreSQL major version, 13 through 18, so you can back up and restore servers of any of those versions from one appliance
  • nginx on port 80 as the only network-facing surface, reverse proxying to PG Back Web on 127.0.0.1:8085
  • A per-VM administrator password and a per-VM encryption key, both generated on first boot and never shared between instances
  • A /healthz endpoint served by nginx and the PG Back Web /api/v1/health endpoint, both unauthenticated, for load balancer and uptime probes
  • A built-in self test at /usr/local/sbin/pgbackweb-selftest.sh that proves the per-VM credentials authenticate through the public listener
  • The complete AGPL-3.0 corresponding source on disk, with a checksum manifest, the licence text and a written offer
  • An Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security updates enabled
  • The Azure Linux Agent for cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with a 24-hour response SLA

Prerequisites

You need the following:

  • An Azure subscription with permission to deploy virtual machines
  • An SSH key pair for administrative access to the VM as the azureuser account
  • A network security group that allows inbound TCP 80 from the networks that should reach the web interface, and 22 (SSH) from your management network only
  • Network reachability from the VM to each PostgreSQL server that you want to back up, and to your object store endpoint if you use one
  • A VM size of Standard_B2s (2 vCPU, 4 GB RAM) or larger (recommended)

Step 1 - Deploy the virtual machine

Create the VM from the SecureAnyCloud Azure Marketplace image. Adjust the resource group, region and size to suit your environment.

az vm image terms accept --urn sac1647283583153:pgbackweb-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-resource-group \
  --name pgbackweb-01 \
  --image sac1647283583153:pgbackweb-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --storage-sku StandardSSD_LRS \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard
# Open port 80 to the networks that should reach the interface. Keep port 22
# restricted to your management network only.
az vm open-port --resource-group my-resource-group --name pgbackweb-01 --port 80 --priority 1001

Step 2 - Read the credentials generated for your instance

First boot generates the administrator password and the encryption key, writes them where only root can read them, and only then opens port 80. Connect over SSH and read the credentials file:

sudo cat /root/pgbackweb-credentials.txt

The file shows the sign-in URL, the administrator email address and the password generated for this machine. The file has mode 0600 and is owned by root, and its contents are unique to this VM. Verify the permissions:

sudo stat -c '%a %U:%G %n' /root/pgbackweb-credentials.txt

Expected output:

600 root:root /root/pgbackweb-credentials.txt

The encryption key that protects everything you store lives separately, in the service environment file. Only root and the pgbackweb service account can read it. Check the file and the key length:

sudo stat -c '%a %U:%G %n' /etc/pgbackweb/pgbackweb.env
sudo sed -n 's/^PBW_ENCRYPTION_KEY=//p' /etc/pgbackweb/pgbackweb.env | wc -c

The key is 64 hexadecimal characters. The wc -c count includes a trailing newline, so it reports 65.

Step 3 - Confirm the appliance is healthy

Check that every service reached the running state and that nothing except SSH and nginx listens on a routable address.

systemctl is-active pgbackweb-firstboot.service pgbackweb.service postgresql@18-main.service nginx.service

Expected output:

active active active active
ss -ltn

PG Back Web is bound to 127.0.0.1:8085 and PostgreSQL to 127.0.0.1:5432. Neither is reachable from the network, and nginx on :80 is the only public entry point.

Both health endpoints answer without authentication, so you can use them for a load balancer probe:

curl -s http://127.0.0.1/api/v1/health echo
curl -s http://127.0.0.1/healthz

Expected output:

{"server_healthy":true} ok

Step 4 - Sign in to the web interface

Browse to the URL in your credentials file, which is http://<your-vm-public-ip>/. Sign in with the administrator email address and password from the same file.

After you sign in, the Summary dashboard opens. It counts your databases, destinations, backup tasks, executions and restorations, and colours each ring by health.

To prove that the same credentials work from the command line, run the self test. It authenticates with a session cookie, exactly as a browser does, rather than a bare token.

sudo /usr/local/sbin/pgbackweb-selftest.sh

Expected output:

SELFTEST_OK: cookie-authenticated dashboard access verified for <PGBACKWEB_ADMIN_EMAIL>

After your first sign-in, change the administrator email address and password to your own from Profile in the navigation rail.

Step 5 - Register a database to back up

Open Databases and choose Add database. Give the connection a name that you will recognise. Pick the major version of the server you are connecting to, which decides the pg_dump binary that PG Back Web uses. Then paste a standard PostgreSQL connection string.

postgresql://backup_user:<your-database-password>@<your-database-host>:5432/orders?sslmode=require

PG Back Web tests the connection when you save it and shows a green indicator when the connection is healthy. It encrypts the connection string with your instance's own key before writing it to disk, and the interface shows the string only in masked form.

The role that you connect as needs permission to read everything that you want in the dump. If the role cannot read a table, pg_dump stops with a permission error, and PG Back Web records the run as failed with that message attached, so failures are visible rather than silent.

Step 6 - Choose where dumps are written

PG Back Web writes each dump either to a Local directory on this VM or to any S3-compatible object store.

Local destinations are written under /backups. The pgbackweb service account owns this directory and is the only account that can read it. Check the directory:

sudo stat -c '%a %U:%G %n' /backups

Expected output:

750 pgbackweb:pgbackweb /backups

Local dumps share the operating system disk. For anything beyond light use, attach and mount a data disk at /backups, or send dumps to object storage instead.

For an S3-compatible destination, open Destinations and choose Add destination. Enter the bucket name, region, endpoint, access key and secret key. You can reach Azure Blob Storage this way through its S3-compatible gateways, as well as AWS S3, MinIO, Backblaze B2 and Cloudflare R2. PG Back Web encrypts the access key and secret key with your instance's own key before storing them.

Step 7 - Create a backup task and run it

Open Backup tasks and choose Create backup task. Select the database and the destination, name the task, and give it a cron schedule and a time zone. Retention in days sets how long PG Back Web keeps each dump before it deletes the dump. The pg_dump option switches on the same form map directly onto the corresponding pg_dump flags.

You do not have to wait for the schedule. Use the row menu and choose Run manually to take a backup immediately. Executions records every run, whether the scheduler or you started it. Each record shows the status, the database and destination used, the start and finish times, the duration and the size of the dump.

To read the same record and the file that was written from the command line, run:

sudo -u postgres psql -w -tA -d pgbackweb -c "SELECT status, coalesce(message,''), pg_size_pretty(file_size), path FROM executions ORDER BY updated_at DESC NULLS LAST LIMIT 1"

Step 8 - Restore

You can restore every successful execution from the Executions page. Use the row menu to choose the restore action, then pick the database to restore into. PG Back Web streams the stored dump back through the psql binary that matches the version of that database. It records the attempt under Restorations, so restores are auditable in the same place as backups.

The appliance carries the client tools for every supported major version. You can therefore restore a dump taken from one server into a server of a different major version, as long as PostgreSQL itself accepts the target version. To list the installed pg_dump binaries, run:

ls -1 /usr/lib/postgresql/*/bin/pg_dump

Step 9 - Back up the encryption key

This is the most important operational step on this appliance.

PBW_ENCRYPTION_KEY in /etc/pgbackweb/pgbackweb.env decrypts every database connection string and every object store credential that PG Back Web holds. If you rebuild this VM, or restore its disk without that file, PG Back Web starts but cannot decrypt anything it stored earlier. You then have to enter every registered database and destination again.

Copy the key to a place where your team already keeps secrets, such as Azure Key Vault, before you rely on the instance. To print the key, run:

sudo sed -n 's/^PBW_ENCRYPTION_KEY=//p' /etc/pgbackweb/pgbackweb.env

Treat the value as you would a password manager master key.

Step 10 - Security notes

The image is hardened before it reaches you. This step describes what that covers and what remains your responsibility.

The service refuses to start on a weak encryption key. A guard that runs before the application on every start enforces this. To see the guard, run:

systemctl cat pgbackweb.service | grep -E 'ExecStartPre|ConditionPathExists'

The account creation route that upstream leaves reachable is closed at the proxy. This appliance creates its administrator during first boot, so the route has no further legitimate use. To confirm that the route is closed, run:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/auth/create-first-user

Expected output:

404

The login form is rate limited by client address. nginx replaces any client-supplied X-Forwarded-For header with the real peer address, so a client cannot forge a fresh rate limit bucket or a false address in the authentication log.

Two decisions remain yours:

  • Terminate TLS. The appliance serves plain HTTP on port 80. Before you expose it beyond a trusted network, put it behind Azure Application Gateway, Azure Front Door or a load balancer with your own certificate, or install a certificate directly into nginx. Passwords and session cookies should not cross an untrusted network in clear text.
  • Restrict who can reach port 80. Keep the network security group as tight as your environment allows.

Step 11 - Licence and source

PG Back Web is free software under the GNU Affero General Public License, version 3. Section 13 of that licence requires that anyone who interacts with the program over a network is offered its corresponding source. This image therefore carries the source on disk instead of relying on a remote location. It is the exact tree that the shipped binary was built from. To verify it against its manifest, run:

cd /usr/share/pgbackweb/source && sudo sha256sum -c SHA256SUMS

Expected output:

pgbackweb-v0.5.1-source.tar.gz: OK

The full licence text is at /usr/share/pgbackweb/LICENSE, and the written offer is at /usr/share/pgbackweb/README.source. SecureAnyCloud is not affiliated with or endorsed by the PG Back Web project.

Troubleshooting

The web interface does not answer on port 80. Check that first boot completed. Until first boot has written the per-VM encryption key and created the administrator, PG Back Web and nginx deliberately stay stopped.

systemctl is-active pgbackweb-firstboot.service
sudo journalctl -u pgbackweb-firstboot.service --no-pager -n 30

PG Back Web will not start. The startup guard refuses an encryption key that is missing, empty, still the SecureAnyCloud build-time value, or a published default. It reports which condition failed.

sudo journalctl -u pgbackweb.service --no-pager -n 30 -b

A backup run failed. PG Back Web records the failure reason from pg_dump against the execution and shows it in the interface. The most common cause is a role that lacks read permission on a table in the target database.

sudo -u postgres psql -w -tA -d pgbackweb -c "SELECT status, coalesce(message,'') FROM executions ORDER BY updated_at DESC NULLS LAST LIMIT 3"

A database shows as unhealthy. PG Back Web tests each registered connection periodically. Confirm that this VM can reach the server and that the credentials and sslmode in the connection string are correct.

You have lost the administrator password. The appliance ships with a password reset tool that generates a new random password for an existing account. Print the reset command:

sudo systemctl show -p Environment pgbackweb.service >/dev/null 2>&1; echo "run: sudo -u pgbackweb env \$(sudo grep -v '^#' /etc/pgbackweb/pgbackweb.env | xargs) /opt/pgbackweb/change-password"

Run the printed command and enter the account email address when prompted. The tool then prints a new password.

Support

SecureAnyCloud provides 24/7 support with a 24-hour response SLA. Contact support through the Azure Marketplace listing or at secureanycloud.com.

Deploy on Azure

Launch PG Back Web 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

Deploy on Azure