Cloud
Everything production needs — servers, database, sites, files, email, AI, DNS, backups, and spending limits — in one AWS organization, defined as code in the repo.
The problem
A new app needs a dozen services before its first user signs up. Each one usually comes from a different vendor: one hosts the frontend, one runs the backend, one holds the database, and others send email, store files, serve DNS, and answer AI calls.
Every vendor is another account, another bill, another dashboard, and another set of terms. Your data ends up in five places, and nobody can say what the whole thing costs in a month.
What Bones does
Everything Bones runs lives in one AWS organization. It's one vendor, one bill, and one sign-in. Every piece is defined in infra/ as AWS CDK code, and every merge to main deploys it. See CI/CD.
| Job | Service | What it does for Bones |
|---|---|---|
| Run the backend | EC2, or ECS on Fargate | Runs the backend/Dockerfile image. One server at the smallest size, containers when you grow |
| Hold the image | ECR | Stores each backend image, tagged with the commit that built it |
| Store data | RDS for PostgreSQL | Your users, organizations, and content, in a database you own |
| Store files | S3 | Uploads, avatars, blog bodies and media, terms, archived logs, and the built sites |
| Serve the sites | CloudFront | The web app, marketing site, blog, docs, and API, each on its own hostname, with security headers |
| Names and HTTPS | Route 53, Certificate Manager | DNS for your domain, and one certificate for it and every subdomain, renewed on its own |
| Send email | SES | Verification, password reset, and welcome mail from your own domain, with DKIM |
| Answer chat | Bedrock | The chatbot's model, called from your own account |
| Keep secrets | Systems Manager Parameter Store, Secrets Manager | Production's settings and the database password. Nothing secret sits in the repo or in GitHub |
| Keep backups | AWS Backup, KMS | Daily database backups, copied to a locked vault in a separate account |
| Cap spending | Budgets, Cost Explorer | Alerts at $50, $120, and $150 a month, anomaly detection, and a cutoff at $150 |
| Separate and sign in | Organizations, IAM Identity Center, IAM | One account per job, one sign-in across them, and guardrails no one can switch off from inside |
| Let CI deploy without keys | IAM with GitHub's OIDC | GitHub Actions gets short-lived credentials for one run, and stores none |
Three things make it hold together:
- One account per job. DNS and the sites, backups, and production each live in their own account. A mistake in one can't reach the others, and a backup survives the account it came from.
- Guardrails above the accounts. Organization policies keep every account in approved regions and stop one from leaving the organization. They apply even to an administrator inside the account.
- Local stand-ins, same API. On your machine, S3 is RustFS, SES is Mailpit, and Bedrock is Ollama. The code only talks to the standard API, so an environment variable picks which one answers. See AWS.
What it costs
The size of production is one setting in infra/app/environments.ts:
| Tier | About | Runs on | Good for |
|---|---|---|---|
| Starter | $33/month | One server, one small database | Up to about 1–3k monthly users |
| Growth | $100/month | Two containers behind a load balancer | About 10–25k monthly users |
| High availability | $275/month | Up to four containers, a standby database, a firewall | Around 100k monthly users |
That's the cost while idle. Email is $0.10 per 1,000, and the chatbot is about $0.0001 a message, capped at $10 a month across the app.
A tier that costs as much as the budget or more fails before it deploys, so moving up to High availability means raising the $150 cutoff first.
What isn't done yet
- One environment. There's production and your machine. Staging is planned, not built.
- One server on Starter. A deploy or a server failure means a short outage. Growth removes it.
- No monitoring. Nothing alerts when the backend errors or slows down. Logs are archived to S3, not searched.
- Email is in the SES sandbox until AWS grants production access, so it only reaches verified addresses.
Two services sit outside AWS on purpose. GitHub holds the code and runs CI/CD, and Google handles "Continue with Google" sign-in.
How it compares
| Option | Trade-off |
|---|---|
| A vendor per job (Vercel, Neon, Resend, …) | Each one is easy to start, but you manage every account, bill, and set of terms |
| An all-in-one platform (Supabase, Firebase) | One vendor, but its own APIs, and leaving means rewriting the parts that use them |
| AWS set up by hand | One vendor and standard APIs, but weeks of console work that no one can review or repeat |
Bones is the third option with the setup already written. It's in the repo, reviewed in pull requests, and deployed the same way every time.