Deploy IOanyT Secure PostgreSQL 16
From subscribing on AWS Marketplace to a running, secured instance — plus day-2 operations.
Operating system
Ubuntu 24.04 LTS
SSH user
ubuntu
Ports
5432, 22
| Operating system | SSH user | Ports | Database |
|---|---|---|---|
| Ubuntu 24.04 LTS | ubuntu | 5432 (PostgreSQL), 22 (SSH) | appdb, user appuser |
This guide takes you from an AWS Marketplace subscription to a working, secured database. There is no manual setup: the instance creates its database, TLS certificate and passwords on first boot. Most of the work is retrieving your credentials and connecting.
Prerequisites
- An AWS account with permission to subscribe in AWS Marketplace and launch EC2 instances
- An EC2 key pair for SSH
- A network path to port 5432 from your application or workstation (same VPC, VPN or bastion host)
- A PostgreSQL client such as
psql(version 12 or newer)
Step 1 — Subscribe and launch
Using the AWS console
- Open the IOanyT Secure PostgreSQL 16 listing in AWS Marketplace and choose Continue to Subscribe.
- Accept the terms, then choose Continue to Configuration.
- Pick the latest version and your Region, then choose Continue to Launch.
- Choose Launch through EC2, then pick an instance type (t3.medium or larger), your VPC and subnet, and your key pair.
- Create or pick a security group as described in Step 2, then launch.
Using the AWS CLI
aws ec2 run-instances \
--image-id <AMI_ID_FROM_MARKETPLACE> \
--instance-type m7i.large \
--key-name <YOUR_KEY_PAIR> \
--security-group-ids <YOUR_SECURITY_GROUP> \
--subnet-id <YOUR_SUBNET> \
--metadata-options HttpTokens=required \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=secure-postgres}]'
Step 2 — Open the right ports
| Port | Protocol | Purpose | Allow from |
|---|---|---|---|
| 5432 | TCP | PostgreSQL (TLS only) | Your application servers’ security group or CIDR |
| 22 | TCP | SSH administration | Your admin IP or bastion host only |
Never open 5432 or 22 to 0.0.0.0/0. Restrict both ports to the networks that need them.
Step 3 — Connect over SSH
ssh -i "your-key.pem" ubuntu@<INSTANCE_IP>
If you get a permission error on the key file, run chmod 400 your-key.pem. Only the ubuntu user can log in over SSH.
First-boot setup usually finishes within a few minutes of launch. To check:
systemctl status ioanyt-postgres-firstboot --no-pager
test -f /var/lib/ioanyt/firstboot.done && echo "Setup finished"
When setup has finished, the service shows active (exited).
Step 4 — Get your credentials
sudo cat /root/ioanyt-credentials.txt
The file contains three values, all unique to this instance:
postgres_password: the PostgreSQL superuserappuser_password: the application user, which owns theappdbdatabasebackup_passphrase: the encryption key for backups (Step 7)
Store these in your secrets manager now. Without
backup_passphrase, backups can’t be restored.
Step 5 — Connect to the database
psql "host=<INSTANCE_IP> user=appuser dbname=appdb sslmode=require"
Application connection string:
postgresql://appuser:<APPUSER_PASSWORD>@<INSTANCE_IP>:5432/appdb?sslmode=require
To also verify the server certificate, copy it from the instance and use sslmode=verify-ca:
# on the instance
sudo cat /etc/postgresql/16/main/tls/server.crt
# on your client, save it as server.crt, then:
psql "host=<INSTANCE_IP> user=appuser dbname=appdb sslmode=verify-ca sslrootcert=server.crt"
For administration on the instance itself, use the local superuser:
sudo -u postgres psql
The postgres operating-system account has no login shell by design, so sudo su - postgres doesn’t work. Use sudo -u postgres <command> instead.
Step 6 — Change passwords safely
Use psql’s \password command. It sends only a hashed value to the server:
sudo -u postgres psql -c '\password appuser'
sudo -u postgres psql -c '\password postgres'
Avoid
ALTER ROLE ... PASSWORD '...'in plain SQL. pgAudit logs role statements, so the new password would be written to the PostgreSQL log.
Step 7 (optional) — Turn on encrypted backups to S3
Backups use pgBackRest with AES-256 encryption: a weekly full backup (Sunday 02:00 UTC), daily incremental backups, and continuous WAL archiving. The last two full backups are kept.
1. Create an S3 bucket and an IAM role
Create a private bucket (block all public access), then an IAM role for EC2 with this policy. Replace <BUCKET> with your bucket name:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetBucketLocation"],
"Resource": "arn:aws:s3:::<BUCKET>"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::<BUCKET>/*"
}
]
}
Attach the role to the instance: in the EC2 console choose Actions → Security → Modify IAM role.
2. Enable backups
sudo ioanyt-enable-backups <BUCKET>
# if the bucket is in a different Region than the instance:
sudo ioanyt-enable-backups <BUCKET> <REGION>
The command configures pgBackRest, turns on WAL archiving, checks access to the bucket and enables the schedule. If any check fails, it turns archiving back off so the disk can’t fill up, and tells you what to fix.
3. Check and run backups
sudo -u postgres pgbackrest --stanza=main info
# take a full backup now:
sudo -u postgres pgbackrest --stanza=main --type=full backup
Write down this instance’s ID (for example
i-0abc123...) together withbackup_passphrase. Backups are stored unders3://<BUCKET>/pgbackrest/<INSTANCE_ID>/, and you need both to restore.
Restore a backup onto a new instance
Use this to recover from a lost instance, or to move to a new instance or a newer image version.
-
Launch a new instance from this product with the same IAM role attached, and wait for first-boot setup to finish (Step 3).
-
Stop PostgreSQL:
sudo systemctl stop postgresql@16-main -
Create the pgBackRest configuration directory with the right owner and permissions:
sudo install -d -m 0750 -o root -g postgres /etc/pgbackrest -
Create
/etc/pgbackrest/pgbackrest.confusing the old instance’s ID and backup passphrase:sudo tee /etc/pgbackrest/pgbackrest.conf >/dev/null <<'EOF' [global] repo1-type=s3 repo1-s3-bucket=<BUCKET> repo1-s3-region=<REGION> repo1-s3-endpoint=s3.<REGION>.amazonaws.com repo1-s3-key-type=auto repo1-path=/pgbackrest/<OLD_INSTANCE_ID> repo1-cipher-type=aes-256-cbc repo1-cipher-pass=<OLD_BACKUP_PASSPHRASE> repo1-retention-full=2 start-fast=y [main] pg1-path=/var/lib/postgresql/16/main EOF sudo chown root:postgres /etc/pgbackrest/pgbackrest.conf sudo chmod 0640 /etc/pgbackrest/pgbackrest.conf -
Restore and start PostgreSQL:
sudo -u postgres pgbackrest --stanza=main --delta restore sudo systemctl start postgresql@16-main -
Check the data. Connect with the old instance’s
appuserpassword, because the restored database keeps its original roles and passwords:psql "host=<NEW_INSTANCE_IP> user=appuser dbname=appdb sslmode=require" -c '\dt' -
To back up the new instance, run Step 7 on it. Its backups go to a new folder named after the new instance ID.
Tested: this procedure was run end to end on the release image, including WAL replay of changes made after the last full backup.
Day-2 operations
Manage the service
sudo systemctl status postgresql@16-main --no-pager
sudo systemctl restart postgresql@16-main
Logs and audit trail
PostgreSQL and pgAudit write to /var/log/postgresql/postgresql-16-main.log. Audit entries start with AUDIT:. To keep them centrally, forward this file with the CloudWatch agent or your SIEM.
sudo grep "AUDIT:" /var/log/postgresql/postgresql-16-main.log | tail
Patching
Apply operating-system and PostgreSQL minor updates in a maintenance window, then reboot if the kernel was updated:
sudo apt update && sudo apt upgrade
Upgrading to a new image version
Back up the current instance (Step 7), launch the new version, restore onto it (see Restore), test, then switch your application’s connection string.
Hardening you should know about
- SSH users: only
ubuntumay log in. To allow another user, add it to theAllowUsersline in/etc/ssh/sshd_configand reload SSH. - Disabled filesystem modules: NFS (including Amazon EFS), FUSE and overlay (used by Docker and Podman) are disabled. To re-enable them, delete
/etc/modprobe.d/ioanyt-cis-unused-fs.confand reboot. - /tmp is mounted with
noexec: programs can’t run from it. - Root is locked. Use
sudofrom theubuntuaccount.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Connection times out on 5432 | Security group or network path | Allow 5432 from your client’s network (Step 2), and check routing or VPN. |
no pg_hba.conf entry ... no encryption | Client tried a plain-text connection | Add sslmode=require (or stricter) to the connection. |
password authentication failed | Wrong password, or the client doesn’t support SCRAM | Copy the password from /root/ioanyt-credentials.txt. Use a client built for PostgreSQL 10 or newer. |
/root/ioanyt-credentials.txt doesn’t exist yet | First-boot setup still running or failed | Wait a few minutes. Then check sudo journalctl -u ioanyt-postgres-firstboot. |
ioanyt-enable-backups fails | Missing IAM role, wrong bucket or Region | Attach the IAM role, check the bucket name and Region, then run the command again. Archiving was switched back off automatically. |
SSH Permission denied | Key file permissions, or wrong user | Run chmod 400 your-key.pem and log in as ubuntu. |
sudo su - postgres fails | The postgres account has no login shell | Use sudo -u postgres psql instead. |
| EFS/NFS mount or Docker fails | Filesystem modules disabled by CIS hardening | See “Hardening you should know about”. |
Support
Email aws-marketplace-support@ioanyt.com. We reply within one business day. Include your instance ID and Region.
See also the product page and the changelog.