Deploy PostgreSQL on a Cube
Install PostgreSQL 16 on a Cube, create a database and a user, and connect two ways — an SSH tunnel while you develop, and an IP-allow-listed TCP port mapping for a deployed app.
A Cube is a real Linux machine with root access, so running PostgreSQL on one is the same job it would be on any VPS — with one difference worth getting right from the start: how you reach the database from outside it.
By the end of this guide you will have PostgreSQL 16 running on a Cube, a database and user created, and both ways of connecting to it:
- An SSH tunnel — nothing is published at all. Ideal while you are developing at your own machine.
- An IP-allow-listed TCP port mapping — a real endpoint your deployed app can hold a connection pool against, reachable only from the addresses you name.
Pick one; you do not need both. The thing to avoid is the third option — publishing the port with no allow-list at all.
Before you start
- A Krova Cloud space with some credit. The Cube used here costs about $0.0065/hour, or roughly $4.75/month if you leave it running.
- An SSH key pair. If you do not have one, run
ssh-keygen -t ed25519and press Enter at each prompt. - A
psqlclient on your own machine, to test the connection at the end. On macOS,brew install libpq; on Debian or Ubuntu,sudo apt install postgresql-client.
This guide fits inside the Welcome tier, so a brand-new account can follow it start to finish with nothing unlocked.
Step 1 — Create the Cube
From your space, go to Cubes and choose New Cube. The settings that matter here:
- Image —
Ubuntu 24.04. PostgreSQL comes from Ubuntu's own package archive, so the plain image is all you need. The Docker image is only worth picking if you plan to run other services in containers alongside it. - Size — 1 vCPU, 2 GB RAM and 10 GB disk is a comfortable starting point for development and small production workloads, and you can resize later without rebuilding.
- SSH key — paste the contents of your public key file (usually
~/.ssh/id_ed25519.pub). It is written to the Cube at boot. This field is required; the Create button stays disabled until you fill it.
The summary panel shows the exact hourly, daily and monthly cost before you commit. Create the Cube and it boots in seconds.
Step 2 — Connect over SSH
Open the Cube and look at the Connect tab. It shows the exact command for your Cube, including its port:
ssh root@<cube-host> -p <port>If the key you created the Cube with is not your default, point at it explicitly:
ssh -i ~/.ssh/my_key root@<cube-host> -p <port>You can also use the in-browser Terminal button on the Cube page if you would rather not leave the dashboard.
Step 3 — Install PostgreSQL
On the Cube, install the server and its contrib extensions:
apt update
apt install -y postgresql postgresql-contribUbuntu 24.04 ships PostgreSQL 16. The package starts the service and enables it at boot for you — confirm both:
systemctl is-active postgresql # active
systemctl is-enabled postgresql # enabledStep 4 — Create a database and a user
PostgreSQL installs with a postgres superuser and no application database. Create a dedicated role and database rather than letting your app connect as the superuser.
Generate a strong password first, and keep it somewhere safe:
openssl rand -base64 24Then create the role and database:
sudo -u postgres psql \
-c "CREATE ROLE appuser LOGIN PASSWORD '<your-password>';" \
-c "CREATE DATABASE appdb OWNER appuser;"Check it landed:
sudo -u postgres psql -c "\l appdb"The listing should show appdb with appuser as its owner.
Step 5 — Connect from your own machine
Out of the box PostgreSQL listens only on the Cube's loopback interface — you can confirm that with sudo -u postgres psql -tAc "SHOW listen_addresses;", which prints localhost. Nothing outside the Cube can reach port 5432, which is exactly what you want.
To reach it from your laptop, forward the port over SSH. Run this on your machine, not on the Cube:
ssh -L 15432:localhost:5432 root@<cube-host> -p <port>Leave that session open. In another terminal, connect as though the database were local:
psql -h 127.0.0.1 -p 15432 -U appuser -d appdbThe traffic rides inside the SSH connection, so it is encrypted end to end and the database is still not published to the internet. Any tool that speaks PostgreSQL works the same way — point it at 127.0.0.1:15432. The Cube's Connect tab has a ready-made port-forwarding command you can copy and adapt.
Step 6 — Connect a deployed app
The tunnel is the right tool while you are working at your own machine, but it is a foreground SSH session: an app running somewhere else cannot depend on you keeping it open. For that, give the database its own endpoint with a TCP port mapping, and put an IP allow-list on it so only your app can reach it.
Add the mapping
On the Cube's Networking tab choose Add Mapping:
- Cube Port —
5432. - IP Whitelist — the public IP of whatever will connect. Single addresses and CIDR ranges both work, comma- or newline-separated.
Do not leave the allow-list empty. The form warns you that an unrestricted mapping is publicly accessible, and a Postgres port open to the internet is found and brute-forced within hours. If you do not know the connecting IP yet, stay on the tunnel until you do.
Krova assigns a public host port — shown on the mapping as Connect: <cube-host>:<hostPort>. That is the address your app uses; it is not the same number as 5432.
Let PostgreSQL answer on it
The mapping forwards traffic to the Cube, but PostgreSQL is still bound to loopback, so it will refuse the connection until you widen it. On the Cube, edit postgresql.conf:
listen_addresses = '*'Then add a matching rule to pg_hba.conf, so only this database and role may authenticate, and only with a hashed password:
host appdb appuser 0.0.0.0/0 scram-sha-256The 0.0.0.0/0 here is not as broad as it looks — the allow-list on the mapping has already decided who may reach the port at all. This line governs who may then authenticate. Reload and confirm:
systemctl restart postgresql
sudo -u postgres psql -tAc "SHOW listen_addresses;" # *Connect
psql -h <cube-host> -p <hostPort> -U appuser -d appdbYour app connects the same way — this is an ordinary PostgreSQL endpoint, so any driver or ORM works with no special handling.
The allow-list is enforced in the host firewall, ahead of the Cube, so a source that is not on it never reaches PostgreSQL at all: the connection does not get refused, it simply times out. Your app also keeps its real client address — inet_client_addr() reports the connecting IP, not a gateway — so per-client logging and pg_hba.conf rules behave the way you expect.
Keep the allow-list current. If your app moves, or its egress IP changes, update the mapping first — otherwise the connection starts timing out with nothing in the PostgreSQL log to explain it, because the packets never arrived.
What it costs
The Cube in this guide (1 vCPU, 2 GB RAM, 10 GB disk) bills at about $0.0065/hour — roughly $0.16/day. Usage is metered by the minute, so you are not rounded up to the hour.
If you only need the database occasionally, power the Cube off when you are done. A stopped Cube is billed for its disk alone and keeps all its data, so you can start it again later and pick up exactly where you left off — see Cubes for how the two states bill.
Cleaning up
If this was a trial run, delete the Cube from its page. That destroys the disk and everything on it, including the database — take a snapshot first if there is anything you want to keep.
Next steps
- Cubes — resizing, snapshots, and how running versus stopped billing works.
- Custom domains — if the app that uses this database needs to be served over HTTPS.
- CLI — create and manage Cubes like this one from your terminal instead of the dashboard.