A quick note on scope: This post walks through a simple self-hosted Postgres setup on a Krova Cloud Cube — perfect for side projects and small workloads. If you're running production with strict RPO/RTO requirements, skip to the production section. Backups are not optional.
I used to be the person who told everyone to use a managed database.
"Don't self-host Postgres," I'd say. "You're not a DBA. Let AWS handle backups and replication."
Then my managed bill started climbing. And I realized I wasn't using 90% of the features I was paying for. And I started wondering if there was a middle ground between "managed service" and "container on a shared host."
There is. It's called a microVM.
The problem with how we run databases
Databases hold the data you actually care about. Yet we routinely:
Run them on the same server as the app
Leave them in containers on shared kernels
Expose port 5432 to the internet because it's "convenient"
Pay managed service prices for single-node setups that don't need half the features
I've done all of these. Most of us have.
A database doesn't need to be reachable from the internet. It doesn't need to share a kernel with strangers. It needs isolation, stable resources, and a sane backup strategy.
What's a microVM?
A microVM is a tiny virtual machine. It boots its own kernel. It has its own userspace. It's isolated by hardware virtualization, like any real VM.
The difference is speed. Firecracker — the open-source microVM tech from AWS — can boot a VM in under a second. Fast enough that microVMs feel as lightweight as containers, but with real isolation.
container: shared kernel, fast, soft boundary
microVM: own kernel, fast, real VM boundary
managed DB: someone else's problem, expensive, shared infrastructure
Krova Cloud Cubes
Krova Cloud gives you Firecracker microVMs called Cubes. Each one has its own kernel and no public IP by default.
Creating a database Cube:
npm i -g @krovacloud/cli
krova cubes create db-1 \
--cpu 2 \
--ram 4 \
--disk 80 \
--image ubuntu-24.04
krova ssh db-1
root@db-1:~#
No VPC. No security groups. No public IP.
If you prefer the API:
curl -X POST https://krova.cloud/api/v1/spaces/$SPACE/cubes \
-H "X-API-KEY: $KROVA_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "db-1",
"image": "ubuntu-24.04",
"resources": { "vcpu": 2, "ramGb": 4, "diskGb": 80 },
"sshPublicKey": "ssh-ed25519 AAAA...",
"region": "eu-central"
}'
Installing Postgres
Once you're SSH'd in, it's just a normal Ubuntu server:
Bash
sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable postgresql
sudo systemctl start postgresql
Create the user and database:
sudo -u postgres psql -c "CREATE USER app WITH PASSWORD 'your-strong-password';"
sudo -u postgres psql -c "CREATE DATABASE app_db OWNER app;"
Edit /etc/postgresql/16/main/postgresql.conf to listen on the private
interface:
conf
listen_addresses = 'localhost,10.0.0.4'
And /etc/postgresql/16/main/pg_hba.conf to allow the app:
host app_db app 10.0.0.0/24 scram-sha-256
Restart Postgres:
sudo systemctl restart postgresql
Done. Your database is running on a real VM with its own kernel, on a private network.
Connecting from your app
Your app runs in another Cube. You connect using the database Cube's private IP:
DATABASE_URL = "postgresql://app:your-strong-password@10.0.0.4:5432/app_db"
No public IP. No exposed port. The database only talks to your app.
For admin access, open an IP-allowlisted SSH mapping:
krova ports add db-1 \
--protocol tcp \
--port 22 \
--allowed-ips 203.0.113.10/32
Backups
This is where self-hosting gets serious. A managed service gives you backups. A microVM gives you the tools to build your own.
For a small project, a daily pg_dump to encrypted object storage is a starting point:
bash
#!/bin/bash
set -euo pipefail
pg_dump -U postgres app_db | gzip > "/backup/app_db-$(date +%Y%m%d).sql.gz"
# encrypt and upload to your preferred storage
Cost comparison
For 2 vCPU, 4 GB RAM, 80 GB disk:
Managed Postgres->
Approximate monthly cost:$50–200+
Notes:Backups, replication, monitoring included
VPS with public IP->
Approximate monthly cost:$20–50
Notes:Shared kernel, public attack surface
Krova Cloud Cube->
Approximate monthly cost:~$10
Notes:Own kernel, no public IP, per-minute billing
markdown
Prices vary, but the gap is real. Just remember: the Cube is the cheap part. The operational work — backups, patching, monitoring, recovery — is the real cost of self-hosting, regardless of platform.
For a side project or small SaaS, the savings add up. For mission-critical workloads, budget for the operational layer too.
Side project vs. production
The simple setup in this post is perfect for:
- Side projects
- Small SaaS apps
- Learning and experimentation
- Workloads where you want full control
For production, you don't need to leave Krova — you need to add operations on top of it. WAL archiving, PITR, automated failover, and mature monitoring can all run on Cubes. Krova handles the isolation and networking. You handle the database operations.
If your team doesn't have the bandwidth for that operational work, a managed service is the right call. That's a staffing decision, not a platform limitation.
The real reason I switched
It wasn't just cost. It was isolation.
I don't want my database sharing a kernel with random workloads. I don't want port 5432 discoverable on the internet. I don't want to rely on a firewall rule I might misconfigure at 2 AM.
A microVM gives me a real boundary. Krova Cloud makes it easy.
Try it
If you want to experiment:
npm i -g @krovacloud/cli
krova signup
krova cubes create db-test --cpu 1 --ram 2 --disk 20 --image ubuntu-24.04
krova ssh db-test
Install Postgres, create a database, and notice that nothing is publicly reachable.
Do you self-host Postgres? Use a managed service? I'd love to hear where you draw the line — and why.
Top comments (2)
This is a sensible middle ground, but I’d make the backup section the acceptance test rather than the final checklist item. A daily pg_dump only gives a 24-hour RPO and is useful only if restores are exercised. For anything beyond a disposable project, I’d add encrypted off-host backups, retention, a scheduled restore into a fresh VM, and an alert on both backup age and restore verification. WAL archiving/PITR matters as soon as losing a day is unacceptable. I’d also include OS/Postgres patching, disk saturation, connection count, replication/failover expectations, and documented recovery ownership in the cost comparison. The microVM may cost ~$10; the operational work is the real line item.
you are right. Backups should be the acceptance test, not an afterthought. My pg_dump example was a starting point for small projects, not a production recipe.
Krova Cloud isn't the limitation here; it's the isolation and networking foundation. For production, I'd add WAL archiving, encrypted off-site backups, restore drills, and monitoring , all running on top of Krova Cloud.
Thanks for the feedback. Updating the post to make that distinction clearer.