Fifty sites, one tower
- 50+ sites consolidating
- 3 deployables, one monorepo
- 0 downtime, by design
The problem
The company ran more than fifty WordPress sites. Every one had its own plugins, its own update schedule and its own way of breaking. SEO settings, ad pixels and SMTP were configured by hand, site by site.
Every new site multiplied the maintenance. Every plugin update was a small gamble.
The moves
One monorepo, three deployables
A public web app that serves each tenant on its own domain, an admin dashboard (blog CMS, SEO metadata, Meta and Google Ads settings, SMTP), and background workers, all sharing types and data access.
Workers split by how they hurt the server
A fast worker (low memory, high concurrency) for analytics, email, scheduled publishing, sitemaps and IndexNow. A heavy worker (more memory, low concurrency) for SEO crawls, AI SEO suggestions, media processing and Search Console sync. One noisy job can no longer starve the rest.
Releases nobody has to stay up for
Docker images built in GitHub Actions, deployed through Coolify and Traefik with readiness probes. Database changes follow expand → backfill → contract, and a failed smoke test rolls the release back automatically.
Shared data, kept in its lane
One PostgreSQL database scoped by tenant_id behind PgBouncer, a dedicated Redis, and Garage for S3-compatible object storage.
The outcome
A platform where adding a site is configuration, not a new server.
Two junior developers working inside a clear architecture, with code review and coaching instead of guesswork.
- TypeScript
- Turborepo
- PostgreSQL
- PgBouncer
- Redis
- BullMQ
- Garage S3
- Docker
- GitHub Actions
- Coolify
- Traefik
Note: In progress. Details are kept general on purpose.