The safe default is a public web server and a database that listens only on a private address. The database server keeps a public address for operating-system updates, but the database port is blocked by two firewalls and has no public listener.
What you are building
internet -> web-01 (203.0.113.10 + 10.10.0.10)
|
private network
|
db-01 (203.0.113.20 + 10.10.0.20)
PostgreSQL listens on 10.10.0.20 only
1. Create the private network
Networking in the client portal, then create a network with a range such as
10.10.0.0/24. See Private networks.
2. Deploy the database server with both networks
Leave public IPv4 on and join the private network as 10.10.0.20. The public
path lets the operating system reach package repositories. You will block public
application access before creating the database.
Turning public IPv4 off does not lower the plan price. It also removes internet access: LayerOne does not provide NAT for private networks, and a public address cannot be added to that server later.
3. Deploy the web server on the same network
Leave public IPv4 on and join the same private network as 10.10.0.10.
4. Confirm they can see each other
From web-01:
ping -c 3 10.10.0.20
ip -brief address
5. Set both firewalls on db-01
On the LayerOne Networking tab, set inbound policy to DROP and outbound to
ACCEPT. Allow SSH from your own public address. Allow TCP 5432 only from
10.10.0.10/32.
Apply the same boundary inside the server. For Ubuntu with UFW:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from YOUR.PUBLIC.IP.ADDRESS to any port 22 proto tcp
sudo ufw allow from 10.10.0.10 to any port 5432 proto tcp
sudo ufw enable
Keep the browser console open until you have confirmed a new SSH connection.
6. Install PostgreSQL on db-01
sudo apt update && sudo apt install -y postgresql
Listen on the private address only, never on 0.0.0.0:
sudo -u postgres psql -c "ALTER SYSTEM SET listen_addresses = '10.10.0.20';"
Allow the web server, and nothing else:
echo 'host appdb appuser 10.10.0.10/32 scram-sha-256' \
| sudo tee -a /etc/postgresql/*/main/pg_hba.conf
sudo systemctl restart postgresql
10.10.0.10/32 is one address, not the whole subnet. Widening it to
10.10.0.0/24 would admit every server you ever put on this network.
7. Create the database and user
sudo -u postgres psql <<'SQL'
CREATE USER appuser WITH PASSWORD 'use-a-long-random-one';
CREATE DATABASE appdb OWNER appuser;
SQL
8. Connect from the web server
sudo apt install -y postgresql-client
psql "postgresql://appuser@10.10.0.20/appdb"
9. Verify it is private
From your laptop, not from either server:
nc -zv 203.0.113.20 5432 # db-01's public address
That must fail. From web-01, the same check against 10.10.0.20 must succeed.
Also verify the listening socket on db-01:
sudo ss -lntp | grep 5432
It should show 10.10.0.20:5432, never 0.0.0.0:5432.
If the database must have no public address
Build a dual-homed router or firewall first, enable NAT in that guest, and set it as the private network's gateway. Confirm that a test server can reach package repositories through it before deploying the database without public IPv4. See pfSense, OPNsense and VyOS.
Without that customer-managed route, a private-only server can be reached only
through the browser console or another server on the same private network. It
cannot run apt update, reach an external backup destination, or contact a
public monitoring service.
Notes on this design
- Private-network traffic counts toward transfer usage. Usage includes inbound and outbound traffic across public and private guest interfaces. See Bandwidth.
- Encrypt anyway if the data is sensitive. A private network is not the internet, but it is not encrypted either. Turn on TLS in Postgres for anything regulated.
- You still need backups.
pg_dumpon a schedule, shipped off the server. Nothing on the platform does this for you. - Add both firewall layers on the web server too. Its public services normally need ports 80 and 443; its database client does not need public port 5432.