I built a small internal billing tool for my business. It has one user, very little traffic, and runs as a Dockerized Node.js app with a React frontend and Postgres database. I don't need high availability, scaling, or especially fast performance—being offline for a few hours would be fine—but I do need reliable data protection.
The database tracks invoices and payment information associated with Stripe. Ideally, I need point-in-time recovery rather than only a nightly snapshot. Restoring a day-old database could leave my local records out of sync with payments that Stripe has already processed.
I also generate PDF receipts that are sent to customers, so those files need durable object storage rather than a container filesystem that disappears during redeployments. Finally, I need a daily cron job at a specific time in my timezone because it runs the billing process for a particular day.
My target is under $20 per month. So far, managed Postgres options with point-in-time recovery seem to start around that range. A cheap VPS would cost roughly $8, but then I'd be responsible for configuring backups, storing WAL files, monitoring failures, and regularly verifying that restoration actually works.
Would a cheaper architecture make sense, such as self-hosted Postgres with WAL backups to S3-compatible storage, SQLite with Litestream, or using Stripe as the source of truth and rebuilding local data when needed? Or is roughly $15–$20 per month simply the realistic price for managed Postgres with genuine point-in-time recovery?
5 Answers
Since Stripe should be the authoritative record of payments, you may not need continuous recovery for every local copy of the billing data. Keep the customer-facing PDFs in durable object storage, take regular database dumps, and design the app to reconcile or rebuild its local payment records from Stripe. That architecture could make nightly backups sufficient, but only if the app can safely replay Stripe data and your local database is treated as a derived view rather than the financial source of truth.
Honestly, $15–$20 per month is close to the normal floor for managed Postgres with real point-in-time recovery. The extra cost is buying someone else’s backup infrastructure and reducing the chance that you discover a failed backup only when you urgently need it. The $15 managed Postgres option you found sounds reasonable; I’d choose it unless you’re comfortable owning the backup, alerting, and restore-testing work yourself.
A cheap VPS is fine if you treat it as a deliberate responsibility: configure WAL shipping, keep backups in a separate provider or location, alert when jobs fail, and perform test restores periodically. Without those checks, the apparent savings are mostly just shifting the risk onto yourself.
The main thing you’re paying for is point-in-time recovery. The app hosting and PDF storage can be extremely cheap if you put the PDFs in S3-compatible object storage such as R2, S3, or Tigris. A small VPS can also run the app and Postgres, with WAL archives and regular dumps uploaded to object storage. That can reduce the bill substantially, but you become responsible for monitoring backups and proving that restores work.
Postgres WAL archiving is the key part here. A full backup plus retained WAL files lets you restore to a specific time, but it needs to be configured and tested rather than assumed to be working.
You can also move toward a serverless setup: host the frontend and API on a usage-based platform, store PDFs in object storage, and use a scheduled job for the billing run. Some databases offer scale-to-zero Postgres or low-cost point-in-time recovery, while services such as DynamoDB, D1, or similar options may have inexpensive backup features. The tradeoff is that replacing Postgres with a NoSQL database or SQLite usually requires redesigning the data model and queries.
For a one-user application, SQLite with Litestream sending the write-ahead log to object storage is worth considering. It avoids paying for a separate database service while still giving you an off-machine recovery path. It may not fit if you need multiple writers, advanced Postgres features, or concurrent access, but for a small internal tool SQLite can be a very capable option.
SQLite is especially appealing if this tool could run locally or on a small private machine. You don’t necessarily need a full web-scale database for one person clicking around a few times per day.

That would require some architectural changes, but it could save more than simply switching hosting providers. Make sure reconciliation is idempotent so rerunning it cannot create duplicate invoices or payments.