Infrastructure plan

Single-host deployment (Docker + EC2)

Optional consolidation path: run Next.js and the Hono API on one machine (e.g. AWS EC2, Lightsail, or any VPS) with Docker Compose, a single reverse proxy, and TLS. This page is the plan only; implementation (Dockerfiles, compose, CI) follows in repo work after sign-off.

Why this option exists

The default split stack (e.g. Vercel + Fly.io + Neon) optimises for speed and managed operations. A single-host layout optimises for one bill, one SSH target, predictable networking, and full control — appropriate for early traffic, staging, or teams comfortable running containers on a VM.

Goals and non-goals

  • Goals — One public origin (or one domain + subdomains); reproducible deploy via Compose; secrets only on host or secret store; health checks; documented rollback; DB either managed Neon (simplest) or Postgres in Compose (full self-host).
  • Non-goals (v1 of this topology) — Multi-region HA, Kubernetes, or auto-scaling beyond vertical resize of the VM. Those can follow if traffic demands.

Target topology

Internet → reverse proxy (Caddy or nginx) on ports 80/443 → internal Docker network → web (Next start) and api (Node + Hono, current apps/api). Static HTML docs ship with the Next build under /docs; no separate docs container required.

                    ┌─────────────────────────────────────┐
  HTTPS :443        │  Reverse proxy (Caddy / nginx)      │
  ────────────────► │  · TLS (Let’s Encrypt)               │
                    │  · /     → next:3000                 │
                    │  · /v1/* → api:3001   (path option)  │
                    └──────────────┬──────────────────────┘
                                   │ Docker bridge network
                    ┌──────────────┴──────────────┐
                    ▼                             ▼
            ┌───────────────┐              ┌───────────────┐
            │  web (Next)   │              │  api (Hono)   │
            │  :3000        │              │  :3001        │
            └───────────────┘              └───────┬───────┘
                                                   │
                     ┌─────────────────────────────┴────────────────────────────┐
                     ▼ (optional)                                                ▼
            ┌─────────────────┐                                        ┌─────────────────┐
            │ postgres:5432   │  OR  keep Neon only — API uses        │ Neon (managed)  │
            │ (volume backup) │      DATABASE_URL to Neon host         │ DATABASE_URL    │
            └─────────────────┘                                        └─────────────────┘

Routing: one origin vs API subdomain

Pattern Browser NEXT_PUBLIC_API_URL Notes
Path proxy (recommended for “one place”) https://ardhkumbh2027.com (same as site) Proxy must forward /v1/* to the API container. Next and API share one TLS cert. CORS becomes trivial for browser calls to /v1.
Subdomain https://api.ardhkumbh2027.com Simpler proxy rules; configure CORS on Hono for the web origin. Mobile apps can use the same API URL.

Compose services (planned)

Service Image / build Role
proxy Caddy official image or nginx TLS termination, HTTP/2, routing to web and api.
web Build from apps/web (multi-stage: install, build, run next start) Production Next.js; sync docs at build as today.
api Build from apps/api (npm run buildnode dist/index.js) Hono on Node 20; DATABASE_URL from env.
db (optional) postgres:16 + named volume Only if leaving Neon; otherwise omit and point API at Neon.

Environment variables (host / compose)

Never bake secrets into images. Use a root .env on the server (mode 0600) or a secret manager later.

Variable Service Purpose
DATABASE_URL api Postgres (Neon URI or internal postgres://db:5432/...).
NEXT_PUBLIC_API_URL web (build arg + runtime) Public API base the browser will call — same origin if using path proxy, or https://api… if using subdomain.
NODE_ENV=production web, api Standard production mode.
PORT api Listen port inside container (e.g. 3001); proxy maps to it.
Stripe / Resend / etc. api (later) As checkout and notifications land; same pattern as today’s API-only secrets.

Phased rollout (plan)

  1. Phase 0 — Documentation (this page) — Agree topology, routing choice (path vs subdomain), and whether Postgres stays on Neon or moves in-Compose.
  2. Phase 1 — Container definitions — Add docker/Dockerfile.web, docker/Dockerfile.api, and docker-compose.yml at repo root; local docker compose up smoke test against Neon.
  3. Phase 2 — VM provisioning — EC2 (e.g. Amazon Linux 2023 or Ubuntu LTS), security group (80/443 from world, 22 from admin IP only), install Docker Engine + Compose plugin, attach EBS if needed.
  4. Phase 3 — TLS and DNS — Point A/AAAA (or CNAME) to the VM; Let’s Encrypt via Caddy or certbot; renewals automated.
  5. Phase 4 — Deploy pipeline — GitHub Actions (or manual): build images, push to GHCR or ECR, SSH pull + compose up -d, or use a tiny deploy script. Blue/green optional later.
  6. Phase 5 — Operations — Log rotation (json-file driver limits), unattended upgrades policy, off-site DB backups (critical if self-hosted Postgres), uptime check on /v1/health.

Cutover from split hosting

  1. Stand up single-host staging with the same DATABASE_URL as production (read replica or copy — policy decision).
  2. Run E2E: catalog, cart, checkout stub, docs at /docs.
  3. Lower DNS TTL; switch A/AAAA; monitor errors; keep old Vercel/Fly as instant rollback until stable.

Risks and mitigations

Risk Mitigation
Single point of failure Accept for early stage; snapshot VM + volume; keep Neon backups if using managed DB.
Disk-full / log bloat Compose log limits; monitor disk; rotate application logs if added.
Next + monorepo tracing Build web image from monorepo context with correct outputFileTracingRoot equivalent in Docker build (documented when Dockerfiles land).

Implementation note

The running API in this repo is Node 20 + Hono (@hono/node-server), not Bun — Docker images should use an official node:20-alpine (or slim) base unless the stack changes.