IOanyT Innovations
Deployment & usage guide

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 systemSSH userPortsDatabase
Ubuntu 24.04 LTSubuntu5432 (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

  1. Open the IOanyT Secure PostgreSQL 16 listing in AWS Marketplace and choose Continue to Subscribe.
  2. Accept the terms, then choose Continue to Configuration.
  3. Pick the latest version and your Region, then choose Continue to Launch.
  4. Choose Launch through EC2, then pick an instance type (t3.medium or larger), your VPC and subnet, and your key pair.
  5. 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

PortProtocolPurposeAllow from
5432TCPPostgreSQL (TLS only)Your application servers’ security group or CIDR
22TCPSSH administrationYour 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 superuser
  • appuser_password: the application user, which owns the appdb database
  • backup_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 with backup_passphrase. Backups are stored under s3://<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.

  1. Launch a new instance from this product with the same IAM role attached, and wait for first-boot setup to finish (Step 3).

  2. Stop PostgreSQL:

    sudo systemctl stop postgresql@16-main
    
  3. Create the pgBackRest configuration directory with the right owner and permissions:

    sudo install -d -m 0750 -o root -g postgres /etc/pgbackrest
    
  4. Create /etc/pgbackrest/pgbackrest.conf using 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
    
  5. Restore and start PostgreSQL:

    sudo -u postgres pgbackrest --stanza=main --delta restore
    sudo systemctl start postgresql@16-main
    
  6. Check the data. Connect with the old instance’s appuser password, because the restored database keeps its original roles and passwords:

    psql "host=<NEW_INSTANCE_IP> user=appuser dbname=appdb sslmode=require" -c '\dt'
    
  7. 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 ubuntu may log in. To allow another user, add it to the AllowUsers line in /etc/ssh/sshd_config and 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.conf and reboot.
  • /tmp is mounted with noexec: programs can’t run from it.
  • Root is locked. Use sudo from the ubuntu account.

Troubleshooting

SymptomLikely causeFix
Connection times out on 5432Security group or network pathAllow 5432 from your client’s network (Step 2), and check routing or VPN.
no pg_hba.conf entry ... no encryptionClient tried a plain-text connectionAdd sslmode=require (or stricter) to the connection.
password authentication failedWrong password, or the client doesn’t support SCRAMCopy the password from /root/ioanyt-credentials.txt. Use a client built for PostgreSQL 10 or newer.
/root/ioanyt-credentials.txt doesn’t exist yetFirst-boot setup still running or failedWait a few minutes. Then check sudo journalctl -u ioanyt-postgres-firstboot.
ioanyt-enable-backups failsMissing IAM role, wrong bucket or RegionAttach the IAM role, check the bucket name and Region, then run the command again. Archiving was switched back off automatically.
SSH Permission deniedKey file permissions, or wrong userRun chmod 400 your-key.pem and log in as ubuntu.
sudo su - postgres failsThe postgres account has no login shellUse sudo -u postgres psql instead.
EFS/NFS mount or Docker failsFilesystem modules disabled by CIS hardeningSee “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.