PG Back Web 0.5.2 on AWS User Guide
Overview
This guide covers launching and using PG Back Web 0.5.2 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Adminer & Database Admin and Backup Tools AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other tools in the listing are described on the listing's guide hub.
PG Back Web gives PostgreSQL backups a web interface. You register each database once (PostgreSQL 13 to 18), say when it should be backed up and where the dump goes (a directory on this instance or any S3-compatible bucket), and then watch every run from one dashboard. Retention, one-click download and restore, an execution history and webhook notifications are built in. Each backup is a pg_dump of the database, compressed into a zip file.
The cloudimg image makes PG Back Web safe to run on EC2:
- Your own administrator, no default account. On its first boot each instance creates one PG Back Web administrator with a random password, before the web server starts. The image contains no account and no password. Upstream's "create first user" page, which on a stock install lets the first visitor become the administrator, is closed (PG Back Web 0.5.2 refuses it, and nginx answers it with 404).
- Per-instance secrets. PG Back Web stores every database connection string and every S3 access key it holds encrypted with
PBW_ENCRYPTION_KEY. Each instance generates its own key on first boot, together with its own PostgreSQL database password. The service refuses to start with an empty, short or published key. - HTTPS from the first boot. nginx serves PG Back Web on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS. Requests for a host name the instance does not serve get no answer.
- Everything else on localhost. PG Back Web listens on
127.0.0.1:8085and its own PostgreSQL 18 on127.0.0.1:5432only, withscram-sha-256passwords and no superuser login over the network. - Other web sites cannot act with your login. nginx refuses a backup run, restore, connection test, webhook run or dump download that another web site's page sends in your browser, refuses any form post whose browser
Originis not this instance, and rate limits sign-in attempts per address. PG Back Web records the real client address of each sign-in. - No access to the instance metadata service. PG Back Web and every
pg_dump,psqland webhook request it makes, PostgreSQL and nginx are blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6) and from the special-purpose address ranges no database or bucket uses (multicast, reserved, benchmarking and IETF protocol ranges). A database or webhook pointed at one of those addresses simply fails. Private addresses in your own VPC stay reachable, so PG Back Web can back up RDS and EC2 databases there. This also means PG Back Web cannot use the instance's IAM role: S3 destinations use an access key and secret key (Step 7). - Built from source, checked. PG Back Web 0.5.2 is built from its release source (checksum pinned) with Go 1.27.3 and current versions of its libraries, with no known reachable vulnerabilities at build time. The service runs as the unprivileged
pgbackwebuser with no Linux capabilities. There is no compiler on the image. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: PG Back Web 0.5.2 (/opt/pgbackweb), the goose schema migrator, PostgreSQL 18 for PG Back Web's own data, the pg_dump and psql clients for PostgreSQL 13, 14, 15, 16, 17 and 18 (PGDG packages), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet that can reach the PostgreSQL servers you want to back up (and your S3 endpoint, if you use one).
- A security group that allows TCP 22 (SSH), TCP 443 (PG Back Web) and TCP 80 (redirects to HTTPS) from the networks your administrators work from. PG Back Web needs no other inbound port.
- On each database server, a security group rule and a
pg_hba.confentry that let this instance connect, and a PostgreSQL role that can read everything you want in the dump.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick PG Back Web 0.5.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (a smaller instance also works for a few small databases), your key pair, the subnet and a security group as above. Local backups are written to the root volume, so size it for the dumps you plan to keep there, or send them to S3.
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 \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=50,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=pgbackweb}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| PG Back Web 0.5.2 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot sets the host name, generates the certificate, creates this instance's PostgreSQL cluster, encryption key and PG Back Web administrator, and then starts PG Back Web and nginx:
systemctl is-active pgbackweb-firstboot pgbackweb postgresql@18-main nginx
sudo test -f /var/lib/cloudimg/pgbackweb-firstboot.done && echo FIRSTBOOT_DONE
All four units are active and the first boot marker is present:
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on the network. PG Back Web and its PostgreSQL listen on localhost only:
sudo ss -lntpH | awk '{print $4, $6}' | sed -E 's/users:\(\("([^"]+)".*/\1/' | grep -v '127.0.0.5' | sort -u
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:8085 pgbackweb
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
A health check for a load balancer or uptime monitor answers without signing in:
curl -sk https://127.0.0.1/healthz
ok
Step 4: Get your URL and password
sudo cat /var/lib/cloudimg/pgbackweb-credentials.txt
The file is readable by root only and holds the URL and the administrator's e-mail and password for this instance:
# PG Back Web 0.5.2 - generated on first boot by pgbackweb-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.
PGBACKWEB_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
PGBACKWEB_ADMIN_EMAIL=admin@pgbackweb.local
PGBACKWEB_ADMIN_PASSWORD=<PGBACKWEB_ADMIN_PASSWORD>
#
# Open PGBACKWEB_URL in a browser (self-signed certificate minted on this instance's first boot; port 80 redirects to
# https) and sign in with PGBACKWEB_ADMIN_EMAIL / PGBACKWEB_ADMIN_PASSWORD. Change the e-mail and password from the
# Profile page after the first sign-in. The account-creation page is closed: this is the one administrator.
# PG Back Web's own PostgreSQL, its encryption key (PBW_ENCRYPTION_KEY) and its database password are also unique to this
# instance: /etc/pgbackweb/pgbackweb.env (root only). Back that key up: stored database and S3 credentials are encrypted
# with it. Local backups go to /backups. Instance metadata is blocked for PG Back Web: S3 destinations use access keys.
# Self-test: sudo pgbackweb-selftest Change the served name: sudo pgbackweb-set-hostname <your-domain>
# Forgot the password: sudo pgbackweb-reset-password
# User guide: https://www.cloudimg.co.uk/guides/pg-back-web-0-5-2-on-ubuntu-24-04-aws/
The URL uses the instance's public DNS name from EC2 instance metadata. Opening https://<public-ip>/ sends you on to that name.
The image includes a self test. Through nginx it signs in as the administrator and checks the pages, the closed account creation, the request guards, PG Back Web's privileges, both localhost listeners and the metadata block:
sudo pgbackweb-selftest
SELFTEST-OK https 301 + healthz + health API + sign-in page + anonymous /dashboard 302, create-first-user 404 + exactly 1 user admin@pgbackweb.local + sign-in -> /dashboard, cookie Secure/HttpOnly/SameSite=Lax, About v0.5.2, wrong password refused (200) + 403 cross-site test + 403 cross-origin POST + 444 unknown host + pgbackweb uid pgbackweb, no capabilities, 127.0.0.1:8085 only + PostgreSQL 127.0.0.1:5432 only + metadata 000 inside pgbackweb, nginx and PostgreSQL (control 200) + secret guard OK
Step 5: Sign in
Open the URL from the credentials file. Your browser warns about the self-signed certificate the first time; accept it for this host (Step 11 replaces it with your own). Sign in with the administrator e-mail and password.

The Summary page counts your databases, destinations, backup tasks, executions and restorations and colours each by health. The navigation on the left leads to each of them, to Webhooks, to your Profile and to About.

Change the administrator's e-mail address and password under Profile after your first sign-in. PG Back Web has one administrator; there is no self-registration.
Step 6: Register a database
Open Databases and choose Add database. Give it a name, choose the PostgreSQL version of the server (this decides which pg_dump PG Back Web runs) and enter a connection string, for example:
postgresql://backup_user:<your-database-password>@<your-database-host>:5432/orders?sslmode=require
Test connection checks it before you save. The connection string is encrypted with your instance's key before it is stored, and the list shows it masked. PG Back Web re-tests every database regularly and marks it green or red.

For an Amazon RDS or Aurora PostgreSQL database, use its endpoint as the host, allow this instance's security group on port 5432 in the database's security group, and keep sslmode=require. The role needs read access to every table, sequence and schema you want in the dump: a table it cannot read makes pg_dump stop, and PG Back Web records that run as failed with the error.
Step 7: Choose where backups go
Local. A backup task with the Local destination writes into /backups on this instance, which only the pgbackweb service user can read:
sudo stat -c '%a %U:%G %n' /backups
750 pgbackweb:pgbackweb /backups
Local backups share the root volume. Give the instance a larger volume at launch, mount a separate EBS volume on /backups (owner pgbackweb, mode 750), or use S3.
S3. Open Destinations and choose Add destination: a name, the bucket, its region, the endpoint (https://s3.<region>.amazonaws.com for Amazon S3; MinIO, Backblaze B2, Cloudflare R2 and other S3-compatible stores work too), an access key and a secret key. Both keys are encrypted with your instance's key before they are stored.
PG Back Web on this image cannot use the instance's IAM role: the instance metadata service, where the role's temporary credentials come from, is blocked for PG Back Web so that a database host or webhook URL entered in the interface can never be used to read it. Create an IAM user (or access key) for PG Back Web that can only reach its own bucket, for example:
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::<your-bucket>" },
{ "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::<your-bucket>/*" }
]
}
Step 8: Create a backup task and run it
Open Backup tasks and choose Create backup task. Pick the database and the destination (or Local), name the task, give it a cron schedule and a time zone, and set how many days to keep each backup. The pg_dump options on the form (--data-only, --schema-only, --clean, --if-exists, --create, --no-comments) are passed straight to pg_dump.

To take a backup now, open the task's row menu and choose Run backup now. Executions lists every run, scheduled or manual, with its status, database, destination, start and finish time, duration and file size. The row menu downloads the backup or restores it.

Each backup is a zip file holding dump.sql, the plain SQL output of pg_dump. You can also restore it by hand with the matching psql.
Step 9: Restore
On Executions, open a successful run's row menu and choose Restore, then pick the database to restore into (the same one or another registered database). PG Back Web downloads the backup and runs it through psql for that database's version; Restorations records each attempt and its result. Restore into an empty database, or create the backup with --clean and --if-exists, so existing objects do not clash.
The image carries the pg_dump and psql clients of every version PG Back Web supports, so one instance backs up and restores PostgreSQL 13 to 18:
for v in 13 14 15 16 17 18; do /usr/lib/postgresql/$v/bin/pg_dump --version; done
pg_dump (PostgreSQL) 13.23 (Ubuntu 13.23-1.pgdg24.04+1)
pg_dump (PostgreSQL) 14.24 (Ubuntu 14.24-1.pgdg24.04+2)
pg_dump (PostgreSQL) 15.19 (Ubuntu 15.19-1.pgdg24.04+2)
pg_dump (PostgreSQL) 16.15 (Ubuntu 16.15-1.pgdg24.04+2)
pg_dump (PostgreSQL) 17.11 (Ubuntu 17.11-1.pgdg24.04+2)
pg_dump (PostgreSQL) 18.6 (Ubuntu 18.6-1.pgdg24.04+2)
Webhooks sends an HTTP request to a URL of your choice when a database or destination becomes unhealthy or a backup succeeds or fails, for example to a Slack or Microsoft Teams incoming webhook.
Step 10: Back up the encryption key
/etc/pgbackweb/pgbackweb.env holds this instance's PBW_ENCRYPTION_KEY (64 hexadecimal characters) and PG Back Web's database password. The key decrypts every connection string and S3 key PG Back Web has stored: without it, a copy of PG Back Web's database is unreadable, and every database and destination would have to be entered again. The file is readable by root and the service only:
sudo stat -c '%a %U:%G %n' /etc/pgbackweb/pgbackweb.env
sudo grep -oE '^[A-Z_]+=' /etc/pgbackweb/pgbackweb.env
640 root:pgbackweb /etc/pgbackweb/pgbackweb.env
PBW_ENCRYPTION_KEY=
PBW_POSTGRES_CONN_STRING=
PBW_LISTEN_HOST=
PBW_LISTEN_PORT=
PBW_PATH_PREFIX=
TZ=
Copy the key into the place your team keeps secrets (for example AWS Secrets Manager or a password manager) before you rely on the instance. To print it, run sudo sed -n 's/^PBW_ENCRYPTION_KEY=//p' /etc/pgbackweb/pgbackweb.env in your own session. To protect PG Back Web's own settings and history as well, back up its database and the environment file together:
sudo -u postgres pg_dump -Fc pgbackweb > /root/pgbackweb-db.dump
sudo cp -p /etc/pgbackweb/pgbackweb.env /root/pgbackweb.env.backup
Step 11: Use your own domain and certificate
- In your DNS, point
<your-domain>(an A record) at the instance's public IP address. An Elastic IP keeps that address stable. - Tell the instance to use it. This sets the name nginx accepts and its request guards, and generates a new self-signed certificate for it:
sudo pgbackweb-set-hostname <your-domain>
- For a certificate your browser trusts, put the certificate chain for
<your-domain>in/etc/ssl/pgbackweb/pgbackweb.crtand its private key in/etc/ssl/pgbackweb/pgbackweb.key(root only, mode 600), and runsudo systemctl reload nginx. Alternatively put the instance behind an Application Load Balancer with an AWS Certificate Manager certificate that forwards to port 443 on the instance.
Step 12: Security checks
PG Back Web, PostgreSQL and nginx are blocked from the instance metadata service:
for u in nginx pgbackweb postgresql@18-main; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; done
nginx 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
pgbackweb 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
postgresql@18-main 168.63.129.16/32 169.254.169.254/32 192.0.0.0/24 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 fd00:ec2::254/128
The block applies to the whole service, so every pg_dump, psql and webhook request PG Back Web starts is blocked too: a database registered at 169.254.169.254 fails its connection test, and a webhook to it is never delivered. The same applies to the special-purpose ranges in the list (192.0.0.0/24, 198.18.0.0/15, multicast 224.0.0.0/4 and reserved 240.0.0.0/4).
Private addresses in your own VPC are deliberately not blocked: PG Back Web has to reach them to back up an Amazon RDS, Aurora or EC2 PostgreSQL database in the same VPC. What it can reach there is decided by your security groups and each database's pg_hba.conf, so allow this instance only to the databases it should back up. IPv6 egress is unchanged, for databases and S3 endpoints you reach over IPv6.
PG Back Web runs as pgbackweb with no Linux capabilities and cannot gain any:
P=$(systemctl show -p MainPID --value pgbackweb)
ps -o user= -p "$P"
grep -E '^(CapEff|CapBnd|CapAmb|NoNewPrivs)' /proc/"$P"/status
pgbackweb
CapEff: 0000000000000000
CapBnd: 0000000000000000
CapAmb: 0000000000000000
NoNewPrivs: 1
PG Back Web's PostgreSQL accepts only the pgbackweb role, from localhost, with a password, plus the postgres superuser over the local socket for root's maintenance:
sudo -u postgres psql -w -tA -c "SELECT type, database, user_name, address, auth_method FROM pg_hba_file_rules ORDER BY rule_number"
local|{all}|{postgres}||peer
host|{pgbackweb}|{pgbackweb}|127.0.0.1|scram-sha-256
Signed-out callers are sent to the sign-in page, the account creation page is closed, nginx refuses a backup run that another web site's page sends and a form post from a foreign Origin, and a request for a host name the instance does not serve gets no answer at all:
H=$(sudo sed -n 's#^PGBACKWEB_URL=https://\([^/]*\)/$#\1#p' /var/lib/cloudimg/pgbackweb-credentials.txt)
curl -sk -o /dev/null -w 'signed-out dashboard: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/dashboard"
curl -sk -o /dev/null -w 'create first user: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/auth/create-first-user"
curl -sk -o /dev/null -w 'cross-site backup run: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Sec-Fetch-Site: cross-site' "https://$H/dashboard/backups/00000000-0000-0000-0000-000000000000/run"
curl -sk -o /dev/null -w 'cross-origin POST: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Origin: https://evil.example' "https://$H/auth/login"
curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || true
signed-out dashboard: 302
create first user: 404
cross-site backup run: 403
cross-origin POST: 403
unknown host name: 000
The credentials, the TLS key, the environment file and the backups are readable by root (and the last two by the pgbackweb service user) only:
sudo stat -c '%a %U:%G %n' /var/lib/cloudimg/pgbackweb-credentials.txt /etc/ssl/pgbackweb/pgbackweb.key /etc/pgbackweb/pgbackweb.env /backups
600 root:root /var/lib/cloudimg/pgbackweb-credentials.txt
600 root:root /etc/ssl/pgbackweb/pgbackweb.key
640 root:pgbackweb /etc/pgbackweb/pgbackweb.env
750 pgbackweb:pgbackweb /backups
Other notes:
- Sign-in limits. nginx allows about 10 sign-in attempts a minute per address and answers more with 429; PG Back Web's own limiter allows 5 attempts per 10 seconds per address. Both use the address that connected to nginx: forwarding headers sent by a client are replaced.
- Who can reach it. The administrator can read every registered database through its backups. Open ports 80 and 443 in the security group only to the networks your administrators work from.
- The build.
/usr/share/doc/cloudimg-pgbackweb/BUILD-INFO.txtrecords the release, its checksums, the toolchain and the library versions; the*.go-version-m.txtfiles next to it list every library in each program. - Logs. nginx logs are in
/var/log/nginx; PG Back Web logs to the journal (journalctl -u pgbackweb), including each failed sign-in with its address.
Step 13: Keep it up to date
Ubuntu security updates install automatically. To apply all updates now:
sudo apt-get update && sudo apt-get upgrade -y
New PG Back Web releases arrive as new versions of this listing, built and checked the same way. To move to one, back up PG Back Web's database and /etc/pgbackweb/pgbackweb.env (Step 10), launch the new version, restore both and restart pgbackweb. Read the PG Back Web release notes before you move.
Troubleshooting
The page does not load. Check that first boot finished: until it has created the encryption key and the administrator, PG Back Web and nginx stay stopped on purpose.
systemctl is-active pgbackweb-firstboot
sudo journalctl -u pgbackweb-firstboot --no-pager -n 20 -o cat | tail -5
PG Back Web does not start. Its start-up guard says why in the journal (sudo journalctl -u pgbackweb -b), for example an environment file whose key was emptied or changed to a published example value.
A backup failed. The pg_dump error is shown on the execution. The usual causes are a security group or pg_hba.conf entry that does not let this instance in, a wrong sslmode, or a role that cannot read a table.
Forgot the administrator password. Run sudo pgbackweb-reset-password, enter the administrator's e-mail address, and it prints a new random password:
sudo pgbackweb-reset-password
Support and licensing
PG Back Web is free software under the GNU Affero General Public License version 3 (Eduardo Lat and contributors). As section 13 of that licence asks, the complete corresponding source of the program on this image (the release source, with cloudimg's build changes: newer library versions, the version stamp and one line that tells PG Back Web to take the client address from nginx) is on the instance with a written offer, and you can check it against its manifest:
cd /usr/share/pgbackweb && sha256sum -c SHA256SUMS
source/goose-v3.29.0-source.tar.gz: OK
source/pgbackweb-v0.5.2-source.tar.gz: OK
LICENSE: OK
README.source: OK
The About page in PG Back Web shows the running version and links to the licence and the project's repository.

The licence text is in /usr/share/pgbackweb/LICENSE. goose (the schema migrator) is under the MIT licence, the Go libraries under permissive licences (MIT, BSD, Apache 2.0, MPL 2.0), PostgreSQL under the PostgreSQL licence and nginx under the BSD licence. There is no per user licence fee. The AMI price covers cloudimg's build, hardening, first boot setup and support. cloudimg is not affiliated with or endorsed by the PG Back Web project.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA
The other versions on this listing are on the database admin and backup tools guide.