All case studies

Web · Infrastructure

Three client sites on one VPS, each onboarded with one command

A containerised Next.js + Payload CMS template with automatic SSL, shared Postgres, off-site backups and a documented restore path.

Role
Architecture, deployment, operations
Scale
3 client sites, 1 VPS
Onboarding
One command

Client catalog sites share about ninety percent of their requirements and differ in the last ten. Building each one from scratch wastes the ninety; running each on its own server wastes the hosting. I built a template that treats a new client as a deployment, not a project.

The problem

Every new catalog client meant repeating the same work: provisioning, TLS certificates, a database, an admin panel, image hosting, backups. Done by hand, that is a day of setup per client and a growing number of one-off servers to remember.

It also does not degrade gracefully. Three bespoke deployments means three different things to debug at 2 a.m., and no confidence that any of them can actually be restored.

Constraints

The boundaries that shaped every decision that followed.

One modest VPS

Small clients cannot justify a server each. Multiple tenants had to be isolated from one another while sharing hardware and a Postgres instance.

Non-technical editors

Clients update their own catalogs. That requires a real CMS with media handling, not a repository they file pull requests against.

Recovery has to be proven

Backups that have never been restored are not backups. The restore path had to be scripted and documented, not improvised.

What I built

  1. 01

    One template, many tenants

    A single containerised Next.js 14 and Payload CMS 3 application serves as the template. Each client is a container built from it with its own configuration and its own database inside a shared Postgres — isolated data, shared hardware.

  2. 02

    Routing and TLS without manual steps

    Traefik routes by hostname and issues Let's Encrypt certificates automatically. Adding a domain means adding a label, not editing nginx configuration and remembering to renew a certificate in ninety days.

  3. 03

    Onboarding as a script

    A new client is a scripted operation: create the database, generate configuration, build and deploy. Deployment and updates are the same — the process that puts a client live is the process that updates them, so it gets exercised constantly.

  4. 04

    Media off the application server

    Uploads go to Cloudflare R2 rather than local disk. Containers stay stateless and disposable, and image storage scales independently of the VPS.

  5. 05

    Backup and disaster recovery

    Databases back up daily to R2, off the machine they came from. Restore is a documented script, and the recovery procedure is written down alongside deployment and troubleshooting runbooks — so recovery does not depend on whoever remembers the most.

  6. 06

    Kept current automatically

    Watchtower handles container updates and Uptime Kuma watches every tenant, so a site going down is something I find out about before the client does.

Results

3
Client sites on a single VPS
1
Command to onboard a new client
Daily
Off-site database backups to R2
Auto
TLS issuance and renewal

Built with

  • Next.js 14
  • Payload CMS 3
  • TypeScript
  • Docker Compose
  • Traefik v3
  • Postgres 16
  • Cloudflare R2
  • Uptime Kuma
  • Watchtower

Working on something similar?

Tell me what you are building and where it is stuck. I will tell you honestly whether I am the right person for it — and roughly what it would take.