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 @CodeWizard 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