๐ŸฆŠ FoxyInvoice

Chapter 06 โ€” Accounts, hosting, DNS, email: from zero to a domain

This is the chapter people skip and then suffer: the accounts, the names, the records that turn code on your laptop into a URL someone else can type. It's step-by-step because order matters โ€” several steps depend on earlier ones existing. Budget an afternoon and about the cost of two coffees a month.

The shopping list (in dependency order)

  1. Domain โ€” buy first; everything else hangs off it.
  2. DNS host (Cloudflare, free tier) โ€” points the name at machines and routes mail.
  3. A VPS โ€” one modest Linux box runs the entire system.
  4. Email sending (SES SMTP) โ€” accounts need a verified identity and API keys.
  5. Object storage (S3-compatible) โ€” PDFs and feedback attachments.
  6. Payments (Stripe) โ€” for payment links and subscriptions.
  7. GitHub โ€” code + CI/CD (you have this if you're reading on it).

Each subsection: what to click, what it costs, and the trap we hit.

Domain + Cloudflare

Buy the domain anywhere reputable; point its nameservers at Cloudflare. Two record concepts do all the work in this project:

Cloudflare also gives you Email Routing free: rules like support@yourdomain โ†’ your-real-inbox. The war story from this repo: rules existed, but the destination address had an unverified state โ€” and every feedback notification bounced as a MAILER-DAEMON storm for a day before anyone noticed the pattern. Lesson: email infrastructure needs a delivery test, not just a config save. We now send probe mails on any routing change.

The VPS and the compose file

One Linux box (any provider; ours is a small OVH instance) runs everything as Docker Compose services:

postgres (per product) ยท api (per product) ยท worker (per product) ยท caddy

Two details that earned their place in the repo docs:

Caddy: TLS you never think about

Caddy sits in front, terminates TLS, proxies /api/* to the api container by name, and serves the built SPA from disk. Its superpower is automatic certificates โ€” Let's Encrypt/ZeroSSL issue and renew with zero cron jobs. The trap that cost us an evening: the Caddyfile is bind-mounted read-only as a single file into the container; editing the host file with any inode-replacing tool (sed -i) is invisible to the running container, and reloads happily keep the old config. Fix: edit in place + docker restart caddy. Config-drift bugs look like caching bugs and aren't.

Email: SES and the sandbox saga

Transaction email needs a real SMTP relay (your VPS's port-25 mail will land in spam purgatory). We use AWS SES via SMTP credentials โ€” with the honest story that our production-grade SES lives in an older AWS account while the newer one sits in sandbox (can only email verified addresses). The pragmatic architecture that fell out: the mailer takes host/credentials from env vars โ€” swap relays without code changes. Also: feedback emails set Reply-To to the reporter, so staff replies reach users instead of a dead inbox.

Object storage + payments

S3-compatible storage holds rendered invoice PDFs and feedback attachments (screenshots and screen recordings โ€”โ‰ค 15 MB, with a server-side size guard so a giant recording can't choke memory). Stripe needs only a developer account to start: payment links are created per invoice server-side; the webhook is the source of truth for "paid." Subscriptions (Pro/Business) run through Stripe Checkout so, again, we never see a card number.

Secrets: generated, never committed

A generate-secrets.sh produces random DB passwords, JWT signing keys, and admin passwords at first setup; they live in .env files on the host, referenced by compose. The repo carries a secret-scanning gate (gitleaks) in CI because everyone eventually pastes a key somewhere โ€” the gate turns a bad Tuesday into a red check.

The launch checklist


Recap. Domain โ†’ DNS (grey-cloud!) โ†’ one VPS running compose โ†’ Caddy's auto-TLS โ†’ SES with probes โ†’ storage โ†’ Stripe โ†’ secrets by script. The traps are all configuration drift that looks like caching โ€” until you write probes for each hop.

Next: Chapter 07 โ€” CI/CD: push to main and it's live โ€” the pipeline, its gates, and the three times it fooled us.