Skip to content

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:

PlanPriceBackupsPoint-in-time recovery
Free$0nonenot available
Pro$25/monthdaily, kept 7 days$100/month per 7 days retention
Team$599/monthdaily, 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:

FactValueHow
Production database size49 MBpg_database_size against production
A full dump3.5 MB/var/log/billuu-db-backup.log, 3,734,199 bytes
Free plan database ceiling500 MBpricing page
Free plan egress5 GB per monthpricing page
Current RPOup to 24 hoursnightly at 19:30 UTC
R2 free allowance10 GB, no egress chargeCloudflare

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 state

That 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

  1. Change /etc/cron.d/billuu-db-backup from 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.
  2. 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.
  3. Confirm the weekly restore proof still passes at 20:30 Sunday against an hourly-produced dump.
  4. 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 Implemented
2026-09-18T19:30:20Z uploaded: r2:billuu-pg-backup/db/billuu-db-20260918T193001Z.dump

The 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:

ItemMonthly
billuu-app Standard_B2ls_v2, 2 vCPU / 4 GiB, centralindia$32.70
Disks, two 64 GiB OS disksa few dollars
Public IPs, two static Standard SKUabout $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

  1. Finish the Startups application. It is the only action that changes the number materially.
  2. 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.
  3. Keep the billuu-50 and billuu-150 budget alerts as they are. billuu-50 fires at 80 percent actual and 100 percent forecast, which is the one that will warn us first.
  4. Re-check the real number in the portal in the first week of November. az consumption returns 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

QuestionDecisionCost
Close the 24 hour recovery window to 1 hourHourly dumps to R2$0
Close it to minutesPITRnot taken, $1,500/year
Keep production running after 13 OctoberPay it~$44/month
Reduce that numberMicrosoft for Startups applicationone form
  • 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 work
  • docs/runbooks/database-restore.md
  • CONTEXT.md, the Backups and Azure platform notes sections