Skip to main content

Hosting: VPS, not AWS

:::note What runs today Contabo VPS (x86_64, 4 vCPU, 8 GB, Ubuntu 24.04), Caddy + Let's Encrypt on an sslip.io hostname, brain under systemd, deployed 2026-09-20 (VPS deployment). The 2026-09-19 decision named Hetzner CAX11; the reasoning below is provider-neutral and holds for any small VPS. Litestream backups are configured in deploy/ but not installed yet. :::

Decision (2026-09-19): a small VPS, apps anywhere​

llm_brain is one static Rust binary + one SQLite file + Caddy, single instance. It runs on a small VPS with a protected public HTTPS endpoint; the apps have no location constraint (AWS, another VPS, home) and reach it with per-app keys (Authentication flow).

Why, in short:

  • Traffic is negligible (below): bandwidth costs zero everywhere.
  • Performance is not a criterion: model time (0.5–30 s) dominates. The hop EU → OpenRouter adds ~100–150 ms TTFB anywhere in the EU; an app on AWS Frankfurt calling an EU VPS adds +5–10 ms. Streaming pass-through adds < 5 ms; SQLite WAL < 1 ms per write.
  • Cost is a coin flip with the Rust footprint (~20–40 MB RSS): every smallest tier fits (a VPS ~€4–5, Lightsail $5; EC2 is pricier because of the paid IPv4). Tokens, not hosting, are the dominant cost.
  • What tips it to a VPS: free RAM headroom (in-process embeddings for a semantic cache would need > 0.5 GB), flat pricing with no metered egress (AWS: $0.09/GB after 1 TB), no burstable CPU throttling under the nightly benchmark, in-place rescale.
  • Discarded: Lambda + DynamoDB (≈ $2–3/month, but no local SQLite: budget/keys/usage move to DynamoDB — an architecture change for ~€2 of savings); ECS Fargate (ALB alone ~$16/month).

AWS is the graduation path at the HA step, not the starting point: the binary moves with scp; SQLite → managed Postgres is the real work, wherever it happens.

Traffic estimate​

From the budgets per profile, 2026-09-19:

ProfileRequests/dayTokens/dayBytes/day (≈ 4 B/token)
dev (Claude Code + aider)300–600~12 M input~50 MB
assistant~200 chats~1 M~4 MB
car (1 000 valuations/day)~1 000~3 M~12 MB
market (later)~1 000~3 M~12 MB
Total~3 000/day ≈ 0.03 req/s~20 M/day~80 MB/day ≈ 2.5 GB/month

Even at 10× the machine stays the same. The board now records real per-request numbers; use them instead of this estimate.

Hostname, not a domain​

Caddy's automatic HTTPS (Let's Encrypt) needs a name, not a purchased domain:

OptionCostWhen
sslip.io (brain.1-2-3-4.sslip.io resolves to the IP) — in use0now: a private API nobody has to remember
Subdomain of an owned domain0one A record, if a domain exists
New domain (Cloudflare Registrar / Porkbun)~€10/yearwith the first public app; llm_brain becomes a subdomain

Client keys don't depend on the hostname: renaming is a base_url change.

When to reconsider​

  • An app sustains > ~1 req/s (30× the estimate): more dedicated vCPUs, not a change of cloud.
  • High availability becomes a requirement: SQLite + one instance is no longer enough — an architecture change (managed Postgres, two instances), decided with usage data.
  • SLA-backed uptime is required, or the apps all land on AWS and one provider is wanted: Lightsail $5 runs the same binary, Caddy and Litestream (to S3) with no penalty.