Decision: database recovery window, and the Azure bill after 13 October
Source:
g2labs-in/linode-infra@f039dba·docs/adr/0005-recovery-window-and-azure-budget.md· updated 2026-09-19
Decided 2026-09-19. Supersedes .scratch/postgres-point-in-time-recovery.
Two budget questions with the same deadline, so they belong in one decision.
Part 1. Database recovery window
The correction that decided it
The obvious plan was “move to Supabase Pro, which includes point-in-time recovery.” That is wrong. Read from supabase.com/pricing on 2026-09-19:
| Plan | Price | Backups | Point-in-time recovery |
|---|---|---|---|
| Free | $0 | none | not available |
| Pro | $25/month | daily, kept 7 days | $100/month per 7 days retention |
| Team | $599/month | daily, kept 14 days | $100/month per 7 days retention |
So PITR is not a Pro feature. It is a separate add-on at $100 per month. Pro plus PITR is $125 per month, or $1,500 per year.
Worth stating plainly: on the Free plan, Supabase provides no automatic backups at all. That is why
the nightly pg_dump to R2 exists. It is not belt and braces on top of a vendor backup, it is the
backup.
What the numbers actually are
Measured 2026-09-19:
| Fact | Value | How |
|---|---|---|
| Production database size | 49 MB | pg_database_size against production |
| A full dump | 3.5 MB | /var/log/billuu-db-backup.log, 3,734,199 bytes |
| Free plan database ceiling | 500 MB | pricing page |
| Free plan egress | 5 GB per month | pricing page |
| Current RPO | up to 24 hours | nightly at 19:30 UTC |
| R2 free allowance | 10 GB, no egress charge | Cloudflare |
The decision: do not buy PITR. Make the dumps hourly instead.
Hourly dumps with 30 day retention:
24 dumps/day x 30 days x 3.5 MB = 2.5 GB steady stateThat fits inside R2’s free 10 GB with room to spare, and it costs nothing. It cuts the worst case from 24 hours of lost sales to 1 hour, which is a 24 times improvement for $0.
The $125/month option buys 60 times improvement instead of 24, or in other words: about $1,500 a year to close a gap that is already one hour wide. At seven tenants that is not defensible, and the house rule says buy only when it is buy-it-or-break-it.
What to actually do
- Change
/etc/cron.d/billuu-db-backupfrom daily to hourly. Keep the 19:30 run as the day boundary so the existing weekly restore verification still has a known newest dump to test. - Keep local retention short. 3 days of local copies at 24 dumps a day is 250 MB of disk on a 64 GB volume, which is fine, but do not let it grow.
- Confirm the weekly restore proof still passes at 20:30 Sunday against an hourly-produced dump.
- Watch two ceilings and alert before either bites:
- Database size against 500 MB. 49 MB today, so there is room, but it is the first hard limit that will be hit and it arrives without warning.
- Egress against 5 GB per month. Not currently measured. Worth a metric before it becomes a surprise.
When to revisit
Buy Pro when the database approaches 500 MB, which removes the size ceiling and the pause rule. Buy PITR when a lost hour is a real business problem, meaning when outlets depend on us during service and we can no longer reconstruct the hour from the tills.
That second condition is close to becoming true, and it is worth naming: the desktop POS keeps a
local copy of orders. Once the sync worker is wired up
(brewlabs-billu-frontend#47), a till that was offline holds an independent copy of anything it rang
up, and an hour of server-side loss becomes recoverable by asking the tills to re-send. That makes
hourly dumps more defensible, not less.
One thing to look at
Every nightly run logs a transient failure before succeeding:
ERROR : Failed to copy: NotImplemented: Not Implemented status code: 501, request id: , host id:ERROR : Attempt 1/3 failed with 1 errors and: NotImplemented: Not Implemented2026-09-18T19:30:20Z uploaded: r2:billuu-pg-backup/db/billuu-db-20260918T193001Z.dumpThe retry covers it and the dead-man check would catch a real failure, so nothing is broken today. But a copy that fails on the first attempt every single night is telling us something about the R2 configuration that would be better to understand than to retry past.
Part 2. The Azure bill after 13 October
What it will cost
From docs/azure-free-tier-plan.md, measured against the live subscription on 2026-09-17:
| Item | Monthly |
|---|---|
billuu-app Standard_B2ls_v2, 2 vCPU / 4 GiB, centralindia | $32.70 |
| Disks, two 64 GiB OS disks | a few dollars |
| Public IPs, two static Standard SKU | about $3.65 each after the credit |
| Approximate total | ~$44 |
The $200 credit covers this until around 2026-10-13. After that the card is charged.
The free tier does not change this, and the plan is honest about why: no always-free grant can host Dokploy, Traefik, the API and Redis. The grants remove the next round of spending, not this one.
The decision: pay it, and treat the Startups application as the real lever
Three options were on the table:
A. Pay about $44 per month and continue Cheapest in effort. The money is real but small, and the alternative is a migration project.
B. Shrink the estate to reduce the bill hermes-labs costs roughly its public IP after the credit, about $3.65. Deleting it saves under 10 percent of the bill and costs the AI lab. Not worth it.
C. Move production off Azure, to a cheaper provider A comparable 2 vCPU / 4 GiB host elsewhere costs roughly $5 per month. That is a genuine six times saving, and also a migration project two weeks after the last migration. Revisit at a natural boundary, not now.Take A, and put the effort into the Microsoft for Startups application, which is already pending with the owner. A successful application replaces the whole question with credits. That is the highest return available here and it costs one form.
What to do
- Finish the Startups application. It is the only action that changes the number materially.
- Move the account to pay-as-you-go before 13 October. It is required either way, and it is also what the on-demand Windows test VM needs before it can be created at all.
- Keep the
billuu-50andbilluu-150budget alerts as they are.billuu-50fires at 80 percent actual and 100 percent forecast, which is the one that will warn us first. - Re-check the real number in the portal in the first week of November.
az consumptionreturns no amounts on this offer, so the portal is the only place the truth lives, and the estimate above is built from the rate card rather than from a bill.
What not to do
Do not let this become a migration project. The estate moved to Azure on 2026-09-13 and moved its DNS to Cloudflare a week later. A third move in a month would cost more attention than the money saved, and attention is the thing this team has least of.
Summary
| Question | Decision | Cost |
|---|---|---|
| Close the 24 hour recovery window to 1 hour | Hourly dumps to R2 | $0 |
| Close it to minutes | PITR | not taken, $1,500/year |
| Keep production running after 13 October | Pay it | ~$44/month |
| Reduce that number | Microsoft for Startups application | one form |
Related
docs/azure-free-tier-plan.md, the measured account facts and the grant list.scratch/postgres-point-in-time-recovery, superseded by this document.scratch/azure-free-tier, the account transition workdocs/runbooks/database-restore.mdCONTEXT.md, the Backups and Azure platform notes sections