Deployment guide

PG Back Web 0.5.2

Overview

PG Back Web gives PostgreSQL backups a web interface. You register each database once (PostgreSQL 13 to 18), choose when it is backed up and where the dump goes, either a directory on this instance or any S3-compatible bucket, and 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.

This SecureAnyCloud image runs PG Back Web 0.5.2 on Ubuntu 24.04 on Amazon EC2. It is one version of the SecureAnyCloud Hardened Adminer & Database Admin and Backup Tools AMIs listing on AWS Marketplace, which you choose as the software version when you subscribe and launch. The other tools in the listing are described on the listing's guide hub.

What you get, to make 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 lets the first visitor of a stock install become the administrator. On this image it 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:8085 and its own PostgreSQL 18 on 127.0.0.1:5432 only, with scram-sha-256 passwords 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. It also refuses any form post whose browser Origin is not this instance, and it 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, every pg_dump, psql and webhook request it makes, PostgreSQL and nginx are blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6). They are also blocked from the special-purpose address ranges that 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. PG Back Web therefore cannot use the instance's IAM role, and S3 destinations use an access key and secret key (Step 7).
  • Built from source and 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 pgbackweb user with no Linux capabilities. There is no compiler on the image.
  • A fully patched base. Ubuntu 24.04 LTS is patched 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.conf entry that let this instance connect, and a PostgreSQL role that can read everything you want in the dump.
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, choose your Region, then choose Launch through EC2.

Choose m5.large, your key pair, the subnet and a security group as described above. A smaller instance also works for a few small databases. Local backups are written to the root volume, so size it for the dumps you plan to keep there, or send them to S3.

To launch with the AWS CLI instead, run the command below and 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}]'
2

Connect to your instance

Connect over SSH as the ubuntu user on PG Back Web 0.5.2 on Ubuntu 24.04.

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot sets the host name, generates the certificate, and creates this instance's PostgreSQL cluster, encryption key and PG Back Web administrator. It then starts PG Back Web and nginx. Confirm that the units are active and the first-boot marker is present:

systemctl is-active pgbackweb-firstboot pgbackweb postgresql@18-main nginx
sudo test -f /var/lib/sac/data/pgbackweb-firstboot.done && echo FIRSTBOOT_DONE

All four units report 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. List the listeners:

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 a sign-in:

curl -sk https://127.0.0.1/healthz
ok
4

Get your URL and password

Read the credentials file as root:

sudo cat /var/lib/sac/data/pgbackweb-credentials.txt

Only root can read the file. It holds the URL and the administrator's e-mail address 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://secureanycloud.com/stacks/pg-back-web-0-5-2/

The URL uses the instance's public DNS name from EC2 instance metadata. Opening https://<public-ip>/ redirects you 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
5

Sign in

Open the URL from the credentials file. Your browser warns about the self-signed certificate the first time, so accept it for this host. Step 11 replaces it with your own. Sign in with the administrator e-mail address 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.

After your first sign-in, change the administrator's e-mail address and password under Profile. PG Back Web has one administrator and no self-registration.

6

Register a database

Open Databases and choose Add database. Give it a name, choose the PostgreSQL version of the server, and enter a connection string. The version decides which pg_dump PG Back Web runs. For example:

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

Select Test connection to check 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.

7

Choose where backups go

Local. A backup task with the Local destination writes into /backups on this instance. Only the pgbackweb service user can read that directory:

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. Enter a name, the bucket, its region, the endpoint, an access key and a secret key. Use https://s3.<region>.amazonaws.com as the endpoint for Amazon S3. MinIO, Backblaze B2, Cloudflare R2 and other S3-compatible stores work too. 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 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 reach only 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>/*" } ] }
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, and give it a cron schedule and a time zone. 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 that holds dump.sql, the plain SQL output of pg_dump. You can also restore it by hand with the matching psql.

9

Restore

On Executions, open a successful run's row menu and choose Restore. Pick the database to restore into, either 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 that 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 send an HTTP request to a URL of your choice when a database or destination becomes unhealthy, or when a backup succeeds or fails. You can point one at a Slack or Microsoft Teams incoming webhook, for example.

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 that PG Back Web has stored. Without it, a copy of PG Back Web's database is unreadable, and you would have to enter every database and destination again.

Only root and the service can read the file:

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=

Before you rely on the instance, copy the key into the place your team keeps secrets, for example AWS Secrets Manager or a password manager. 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
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 the name. 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.crt and its private key in /etc/ssl/pgbackweb/pgbackweb.key (root only, mode 600). Then run sudo 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.
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 that 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. Your security groups and each database's pg_hba.conf decide what it can reach there, 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. It also accepts 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, and 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. 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/sac/data/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 only. The last two are also readable by the pgbackweb service user:

sudo stat -c '%a %U:%G %n' /var/lib/sac/data/pgbackweb-credentials.txt /etc/ssl/pgbackweb/pgbackweb.key /etc/pgbackweb/pgbackweb.env /backups
600 root:root /var/lib/sac/data/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, and 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/sac-pgbackweb/BUILD-INFO.txt records the release, its checksums, the toolchain and the library versions. The *.go-version-m.txt files 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.
13

Keep it up to date

Ubuntu security updates install automatically. To apply all updates now, run:

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). Then 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 explains why in the journal (sudo journalctl -u pgbackweb -b). A typical cause is 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 and enter the administrator's e-mail address. 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 is on the instance with a written offer. It is the release source plus SecureAnyCloud's build changes: newer library versions, the version stamp, and one line that tells PG Back Web to take the client address from nginx. 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 use permissive licences (MIT, BSD, Apache 2.0, MPL 2.0), PostgreSQL uses the PostgreSQL licence, and nginx uses the BSD licence.

There is no per-user licence fee. The AMI price covers SecureAnyCloud's build, hardening, first-boot setup and support. SecureAnyCloud is not affiliated with or endorsed by the PG Back Web project.

  • Email: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • Support: 24/7, with a 24-hour response SLA

The other versions on this listing are on the database admin and backup tools guide.

Deploy on AWS

Launch PG Back Web 0.5.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS