Setting Up PostgreSQL on Ubuntu VPS
Before the before you start
I've tried my best to detail everything here. If you notice any issues or things that are wrong, please reach out on X @ldo_dev or by email and let me know and I'll get them sorted asap.
If you think I can improve this in anyway, please let me know also!
Before you start
Buy a vps and figure out your ssh credentials
First Login
ssh into your new setup
ssh root@domain
# or
ssh root@ipUpdate everything
sudo apt update && sudo apt upgrade -ySet the timezone (optional/ depends where you're hosing)
timedatectl set-timezone Europe/London # Change for yours?Create a non-root user (first time setup only)
adduser lukeusermod -aG sudo lukeFrom now on we use the new user
su - lukeInstall Postgres
You can change the version to what you like. I think 18 is the latest, but all my apps are tailored for 17, so that's what I'll use.
Ubuntu repositories can lag - so best to use the official PostgreSQL repo
sudo apt install -y curl ca-certificates gnupg
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/postgres.gpg
echo "deb [signed-by=/usr/share/keyrings/postgres.gpg] \
http://apt.postgresql.org/pub/repos/apt \
$(lsb_release -cs)-pgdg main" \
| sudo tee /etc/apt/sources.list.d/postgres.listInstall updates and PostgreSQL
sudo apt update && sudo apt install -y postgres-17 postgres-client-17Verify installation
psql --versionSanity check it actually installed and is running
sudo systemctl status postgresqlSwitch to the postgres user:
sudo -i -u postgrespsqlExit postgres editor
\q # or "exit" if that doesn't workCreate production and dev roles and databases
Again this is just something I like to do. Production users can only access the production databases and vice versa. Helps to prevent any fuckups in dev lol
Re-enter postgres account
sudo -i -u postgrespsqlYou're now in the postgreSQL editor.
Create Roles (users)
CREATE ROLE prod_user LOGIN PASSWORD 'STRONG_PROD_PASSWORD';
CREATE ROLE dev_user LOGIN PASSWORD 'STRONG_DEV_PASSWORD';Create databases
CREATE DATABASE prod_db OWNER prod_user;
CREATE DATABASE dev_db OWNER dev_user;Lock users to their db only
REVOKE ALL ON DATABASE prod_db FROM PUBLIC;
REVOKE ALL ON DATABASE dev_db FROM PUBLIC;Give users permission to see their db
ALTER DATABASE prod_db OWNER TO prod_user;GRANT ALL PRIVILEGES ON DATABASE prod_db TO prod_user;Exit Postgres Editor
\q # or "exit" if that doesn't workConfigure Postgres to accept remote connections
Edit postgresql.conf
sudo nano /etc/postgresql/17/main/postgresql.confScroll through and find the following line, uncomment and change it
listen_address = '*'Edit pg_hba.conf
This is your holy grail - treat it with care and don't tamper with it
sudo nano /etc/postgresql/17/main/pg_hba.confAdd at the bottom
# PROD
hostssl prod_db prod_user 0.0.0.0/0 scram-sha-256
# DEV
hostssl dev_db dev_user 0.0.0.0/0 scram-sha-256Notes:
hostsslforces TLSscram-sha-256is modern and good- Firewall will do the real IP locking
- 0.0.0.0/0 allows any connection from any IP range. Ideally, once your main hosting is configured (if using a static IP), you'll only allow traffic from there to prevent attacks.
Every time you change pg_hba.conf you will need to restart postgres server
sudo systemctl restart postgresqlEnable TLS
Create the certificate directory
sudo mkdir /etc/postgresql/ssl
sudo chown postgres:postgres /etc/postgresql/ssl
sudo chmod 700 /etc/postgresql/sslGenerate Self Signed Cert (fine for now)
sudo -i -u postgresopenssl req -new -x509 -days 365 \
-nodes -text \
-out /etc/postgresql/ssl/server.crt \
-keyout /etc/postgresql/ssl/server.keychmod 600 /etc/postgresql/ssl/server.keyExit postgres
\q # or "exit" if that doesn't workTell postgres to use the cert
sudo nano /etc/postgresql/17/main/postgresql.confSet:
ssl = on
ssl_cert_file = '/etc/postgresql/ssl/server.crt'
ssl_key_file = '/etc/postgresql/ssl/server.key'Restart service again
sudo systemctl restart postgresEnable Firewall
We'll use UFW (Universal FW)
# If you're allowing all IPs through from previous step, use:
sudo ufw default allow incoming
# Otherwise deny it and follow next steps
sudo ufw default deny incomingAllow SSH
sudo ufw allow from [your_ip] to any port 22Allow Web hosting thorough (my example)
sudo ufw allow from VERSEL_IP_RANGE to any port 5432
sudo ufw allow from SEVALLA_IP_RANGE to any port 5432Regardless of which above option you selected, set outgoing to open
sudo ufw default allow outgoingEnable ufw:
sudo ufw enablesudo ufw statusTest connection locally
psql "postgresql://prod_user@localhost/prod_db?sslmode=require"Connection string example
DATABASE_URL="postgresql://prod_user:PASS@prod.pg.your-infra.com:5432/prod_db?sslmode=require"Note: you might have some issues with url encoding the password (i did). Should be fine, just generate a password that doesn't need encoding - your fav chat bot will do that.
Setup Backups
Install
sudo apt install -y postgresql-clientSetup cron job to run daily
crontab -e0 2 * * * pg_dump prod_db | gzip > /var/backups/prod_db_$(date +\%F).sql.gz