docs / hosting-platform-architecture-plan

Hosting Platform Architecture Plan

Hosting Platform Architecture Plan

The big-picture plan for how Hostking Pro works end to end. Read this if you're new to the team, or if you want to understand the "why" behind any specific decision.

One-paragraph summary

Hostking Pro is a multi-tenant hosting platform. Each tenant gets a dedicated Linux user, a systemd-managed process, an Nginx config in front of it, and an nftables egress block. Sites are deployed via a zip upload, a GitHub webhook, or a CLI command. Everything is orchestrated by a single Node.js Next.js app talking to Supabase (data) and WorkOS (auth).

Layers

1. Edge — Cloudflare

All HTTP traffic terminates at Cloudflare. We use:

  • DNS (with proxying)
  • DDoS protection
  • WAF rules (managed + custom)
  • Origin pull certificate (Cloudflare → hostkingpro.com:443)
  • Rate limiting (free tier: 100 req/min per IP)

Cloudflare caches static assets (.css, .js, .png, .woff2) with Cache-Control: public, max-age=86400. API routes are never cached.

2. Front door — Nginx (one per host)

client → Cloudflare → hostkingpro.com:443 (nginx) → :3002 (Next.js)

Nginx does:

  • TLS termination (Let's Encrypt cert, auto-renewed by certbot)
  • HTTP/2
  • Request body buffering (for upload endpoints)
  • WebSocket passthrough (for SSE deploy logs)

3. App — Next.js on PM2 (one process)

Next.js renders both the marketing site (SSG) and the dashboard (SSR). PM2 manages the process: restart on crash, log rotation, graceful reload. The Next.js server uses Node 20.

API routes live under src/app/api/.../route.ts. Each route is a small, isolated handler. Auth checks happen per-route via the getSession() helper, which reads the WorkOS-issued iron-session cookie.

4. Data — Supabase (Postgres + Auth-via-WorkOS)

Supabase holds the truth:

  • organizations — one per customer org
  • profiles — one per user (WorkOS user id is the primary key, which is text)
  • organization_memberships — many-to-many join
  • site_assets — every site ever deployed
  • deployment_logs — every deploy attempt, with full stdout
  • pods — the host machines that run the tenants
  • audit_log — every privileged action (create site, delete site, invite user, etc.)

We use the service role JWT to bypass RLS for background jobs and webhooks. The per-user server client (created via getServerClient()) respects RLS and is used for everything else.

Auth is not Supabase Auth. We use WorkOS AuthKit. Supabase is data only.

5. Hosts — Pods (Linux machines)

A Pod is a Linux machine (or a container that looks like one). It runs:

  • systemd with one unit per tenant (hostking-<system_username>.service)
  • nftables with one ruleset per tenant (egress block)
  • An Nginx config in /etc/nginx/sites-enabled/<system_username>.conf
  • A tenant home directory at /home/hk_usr_<system_username>/

The Pod scheduler watches pods table for unhealthy Pods and migrates sites off. The provisioner (a separate bash toolkit) creates the tenant home, the systemd unit, and the nftables ruleset on demand.

6. Tenants — `/home/hk_usr_<system_username>/`

Each tenant home contains:

/home/hk_usr_b2222222/
ā”œā”€ā”€ app/                # the deployed code (unpacked zip or git clone)
ā”œā”€ā”€ logs/               # tail-able logs (symlink to systemd journal)
ā”œā”€ā”€ env/                # env vars (file, mode 0600)
ā”œā”€ā”€ data/               # persistent storage for the app
ā”œā”€ā”€ backups/            # nightly tarballs (retention: 7 days)
└── .runtime-state.json # last-known status (written by the app's /healthz)

The systemd unit starts the app on boot, restarts it on crash, and writes its stdout/stderr to the journal.

Data flow: deploy a site

1. user → POST /api/deploy with zip
2. Next.js uploads zip to /tmp/<uuid>.zip
3. unzip → /home/hk_usr_<user>/app/
4. spawn systemd unit → hostking-<user>.service
5. systemd starts the app, runs npm install if package.json changed
6. systemd writes health status to /home/hk_usr_<user>/.runtime-state.json
7. /api/sites/<id>/health polls that file every 30 seconds
8. dashboard sees "active" status, deploy_logs row updated to "succeeded"

Data flow: GitHub webhook

1. user pushes to their repo
2. GitHub sends POST /api/internal/github-webhook
3. Next.js verifies HMAC-SHA256 signature
4. Next.js writes a deploy_logs row (status=queued)
5. Next.js spawns `git pull && restart` via systemd on the assigned Pod
6. deploy_logs row updated as deploy progresses
7. webhook recipient (configured in /dashboard/github) gets a POST with the final result

What could go wrong (and what we do about it)

RiskMitigation
A single site misbehaves (CPU hog, OOM)cgroup v2 limits (memory_limit_mb, cpu_quota_percent). The Pod scheduler evicts if a site exceeds its quota for >5 min.
A single Pod goes downPod scheduler migrates sites off. RTO for a single Pod: 30 minutes.
The whole DB is corruptedNightly pg_dump to S3, 7-day retention. RPO: 6 hours.
The whole box is goneInfra-as-code provisioning script (Phase 4 backlog). RTO: 4 hours.
Bad deploy takes down prodEach tenant's app runs in its own cgroup. A bad Node app cannot exhaust box resources.

Why not Vercel / Render / Fly / etc.?

We considered buying compute from someone else. Decision: no. Reasons:

  1. Pricing scales linearly with usage — they make money when our customers use more resources. We want to make money when our customers use fewer resources.
  2. Cold starts — every PaaS has them. We can promise sub-second cold starts because we control the runtime.
  3. Lock-in — Vercel's pricing API is opaque, their free tier was downgraded last year, their hobby-tier pricing is hidden behind a "Contact Sales" form.
  4. Boring tech — systemd + nginx + nftables is a 20-year-old stack. Our team knows it. We won't be surprised by it.
  5. Multi-cloud — we can move Pods between any VPS provider (Hetzner, OVH, DigitalOcean, AWS Lightsail) without rewriting the platform.

Roadmap (post-GA)

  • Q3 2026: Stripe integration, agency portal
  • Q4 2026: Managed databases, object storage
  • Q1 2027: White-label offering for hosting resellers

Open questions

  • Should we offer cron jobs? (Yes — but as a separate tier, not bundled.)
  • Should we support custom domains out of the box, or punt to a partner? (Custom domains, since Let's Encrypt is free.)
  • What about email forwarding? (Phase 3 of the GA roadmap. Not yet.)
Source: ./docs/hosting-platform-architecture-plan.md
Hostking Pro — multi-tenant hosting platform