ADR-0005: Infrastructure and Tech Stack
Status: Accepted
Date: 2026-01-05
Context: Memory Maker needs a tech stack that supports the MVP while providing a clear migration path for scale. Key considerations include cost at scale (particularly storage egress for PDF downloads), developer velocity, and operational simplicity.
Decision Drivers:
- Cost efficiency at scale (PDFs can be large, downloads are frequent)
- Developer velocity for MVP
- Operational simplicity (small team)
- Clear migration path as usage grows
- Reliability for time-sensitive events (funerals, birthdays)
Decision
Phase 1 (MVP) Stack:
| Component | Choice | Rationale |
|---|---|---|
| Hosting | Netlify → Cloudflare Pages | Netlify for rapid prototyping; migrate to CF once design stabilizes |
| Database | Supabase (PostgreSQL) | Auth + DB + Storage in one, Row Level Security |
| Auth | Supabase Auth | Magic links, social auth, integrated with DB |
| Storage | Supabase Storage | Integrated with auth/RLS, simple for MVP |
| Azure Communication Services | Reliable, cost-effective transactional email | |
| DNS | Cloudflare | DDoS protection, fast propagation, free tier |
| Payments | Stripe | Industry standard, good DX, flexible pricing models |
Migration Timeline:
| Component | Migration | Trigger |
|---|---|---|
| Hosting | Netlify → Cloudflare Pages | After design/approach finalized (early Phase 1) |
| Storage | Supabase → Cloudflare R2 | After PDF generation approach decided |
| Database | Stay on Supabase | Unlikely to need migration |
| Stay on ACS | Unlikely to need migration |
Intent: Move to Cloudflare quickly once we're past rapid iteration. Netlify is for throwaway prototypes only.
PDF Generation (Open Decision):
This is the biggest technical unknown. Options under consideration:
External Service (e.g., DocRaptor, PDFShift)
- Pros: Battle-tested, handles edge cases, no server management
- Cons: Per-document cost, external dependency
Serverless Function (Puppeteer on Netlify/Cloudflare)
- Pros: Full control, no per-document cost
- Cons: Cold starts, memory limits, Chrome binary size
Client-Side (@react-pdf/renderer)
- Pros: No server costs, instant generation
- Cons: Limited styling, browser memory limits, can't use system fonts
Decision deferred to Phase 1 development spike.
Rationale
Why Supabase over Azure SQL/Cosmos?
- Integrated auth: Magic links and social auth out of the box
- Row Level Security: Contributors only see their submissions without app-level enforcement
- Storage integration: File uploads tied to auth automatically
- Cost: More predictable pricing for early-stage SaaS
- DX: Better developer experience for rapid prototyping
Why Cloudflare R2 for Scale?
R2's zero egress fees are critical for a PDF-heavy application:
| Storage | 100GB stored | 1TB egress/month |
|---|---|---|
| AWS S3 | $2.30 | $90.00 |
| Supabase | $2.50 | ~$90.00 |
| Cloudflare R2 | $1.50 | $0.00 |
A popular tribute book could generate thousands of PDF downloads. At scale, egress becomes the dominant cost.
Why Not Start on Cloudflare?
- Netlify has better Astro integration and faster deploys for iteration
- Cloudflare Pages has quirks with SSR that slow development
- Migration is straightforward when costs justify it
- "Prototype fast, optimize later"
Why ACS over SendGrid/Mailgun?
- Already have Azure credits/relationship
- Competitive pricing
- Good deliverability
- Simple API
Consequences
Positive
- Fast time to MVP with familiar tools
- Clear cost triggers for migration decisions
- No vendor lock-in on critical paths
- Storage costs controlled at scale via R2 migration
Negative
- Migration overhead when scaling (estimate: 1-2 days per component)
- Mitigation: Design with migration in mind from day 1
- Two storage systems to potentially manage during transition
- Mitigation: Abstract storage behind service layer
Neutral
- Supabase is a younger platform than AWS/Azure
- Acceptable risk for MVP; can migrate DB if needed (PostgreSQL is portable)
Implementation
Phase 1 Setup:
- Create Supabase project (database, auth, storage buckets)
- Configure Netlify with Supabase environment variables
- Set up Cloudflare DNS pointing to Netlify
- Configure ACS for transactional email
- Set up Stripe in test mode
Storage Architecture:
Supabase Storage Buckets:
├── tribute-photos/ (contributor uploads, private)
├── generated-pdfs/ (final books, private with signed URLs)
└── book-assets/ (admin uploads like cover images, private)
Cloudflare R2 (at scale):
├── pdfs-hot/ (recent PDFs, <6 months)
└── pdfs-cold/ (archived PDFs, 6+ months, Infrequent Access)
Tiered Storage Lifecycle:
| Age | Storage Tier | Access Pattern | Cost |
|---|---|---|---|
| 0-6 months | R2 Standard | Frequent downloads | $0.015/GB |
| 6-12 months | R2 Infrequent Access | Occasional re-downloads | $0.01/GB |
| 12+ months | R2 Infrequent Access | Rare (anniversary, etc.) | $0.01/GB |
PDFs move to cold storage automatically via lifecycle policy. Most tribute books have a "hot period" around the event (birthday, funeral, retirement) then tail off. Anniversary re-downloads are infrequent but should remain accessible.
Lifecycle Policy Logic:
- After 6 months: Move to Infrequent Access tier
- Never delete: Tribute books are sentimental; customers may return years later
- Re-warm on access: If a cold PDF gets multiple downloads, consider moving back to hot
Migration Checklist (when triggered):
- Set up R2 bucket with same structure
- Create signed URL generation for R2
- Migrate existing PDFs (can be done gradually)
- Update PDF generation to write to R2
- Keep Supabase Storage for uploads (integrated auth)
Database Schema Considerations:
-- Store file references, not files
submissions (
id uuid,
photo_urls text[], -- Supabase Storage paths
...
)
books (
id uuid,
pdf_url text, -- Initially Supabase, later R2
pdf_storage text, -- 'supabase' or 'r2' for migration
...
)
Cost Projections
Phase 1 (0-100 books/month):
| Service | Monthly Cost |
|---|---|
| Netlify | $0 (free tier) |
| Supabase | $0-25 (free → Pro) |
| Cloudflare DNS | $0 |
| ACS | ~$5 |
| Stripe | 2.9% + $0.30/transaction |
| Total | ~$30 + payment fees |
Scale (1000+ books/month):
| Service | Monthly Cost |
|---|---|
| Cloudflare Pages | $0-20 |
| Supabase Pro | $25 |
| Cloudflare R2 | ~$15 (storage only) |
| ACS | ~$50 |
| Total | ~$110 + payment fees |
Note: At 1000 books/month with $29 average price, this represents <0.5% of revenue in infrastructure costs.
Related Decisions
- ADR-0001: AI Assistance Philosophy (may need API costs for AI features)
- ADR-0004: Revision Workflow (email notifications via ACS)
Open Questions
- PDF generation approach - Spike needed in Phase 1
- Image optimization - Cloudflare Images or sharp in build?
- CDN for assets - Cloudflare in front of Supabase Storage?
Author: Bert Carroll Reviewers: TBD Last Updated: 2026-01-05