launchthat
Portal V1: The WordPress Era — Building a SaaS on a CMS
Seven years ago I set out to build a multi-tenant platform on WordPress Multisite. All client data lived in one database, third-party glue held everything together, and a $100/mo host still could not keep up. Here is what I learned — and why I had to leave WordPress behind.
This is Part 1 of a four-part series tracing the Portal platform from a WordPress Multisite experiment to a Kubernetes-orchestrated, bare-metal deployment. Each article covers one version — what I built, what broke, and the moment I knew it was time to move on.
The idea
Seven years ago I had a simple pitch: give every client their own branded dashboard where they could manage their business — courses, customers, products, content — all in one place. No more juggling five different SaaS logins. No more copying data between spreadsheets and CRMs. One platform, their logo on it, everything they need behind a single login.
I was not a software developer at the time. I was a WordPress builder. I could spin up a theme, install plugins, wire up custom fields with ACF, and glue services together with Zapier. That skillset got me surprisingly far — far enough to take on real clients and charge real money.
WordPress Multisite was the obvious foundation. It could run multiple sites from a single installation. Each client would get their own "site" within the network, their own subdomain, their own admin panel. WordPress had thousands of plugins for everything I needed. The pitch practically built itself.
The architecture
The setup was straightforward by WordPress standards:
- WordPress Multisite running on managed hosting ($100/month for decent performance)
- One MySQL database backing every client site in the network
- ACF Pro for custom data models — courses, modules, lessons, client profiles
- WooCommerce for any e-commerce features
- Zapier and Make.com bridging WordPress to every external service
The third-party dependency map looked like this:
WordPress Multisite
├── Zapier ──► Pandadoc (disclaimers, contracts)
│ ├► GoHighLevel (CRM, pipelines)
│ ├► Zoho CRM (contact management)
│ └► Mailchimp (email campaigns)
├── Make.com ──► SMS provider (notifications)
│ ├► Google Sheets (reporting)
│ └► Slack (internal alerts)
├── WooCommerce ──► Stripe (payments)
└── Gravity Forms ──► lead capture, onboarding
Every arrow in that diagram was a potential point of failure. And every one of them cost money — Zapier plans, Make.com plans, Pandadoc subscriptions, CRM seats. The platform was not just a WordPress site. It was a fragile web of API connections held together by automation recipes.
What worked
I want to be honest about the wins because they mattered. WordPress Multisite let me ship a working product to real clients in weeks, not months. The plugin ecosystem was genuinely powerful for someone without traditional development skills.
Clients could log in, see their branded dashboard, access their courses, manage basic settings. It worked. Revenue came in. People used the product. For a solo operator with no formal engineering background, that was a real achievement.
The WordPress community also had answers for almost everything. Forum posts, StackOverflow threads, premium plugin documentation — if I got stuck, someone had been stuck before me and written about it.
What broke
The cracks appeared slowly and then all at once.
The single database problem. Every client's data lived in the same MySQL instance. WordPress Multisite creates separate table prefixes per site (wp_2_posts, wp_3_posts), but it is still one database server handling every query. As client count grew, page loads got slower. I did not understand indexes. I did not know how to read a query execution plan. I just knew the platform was slow and the hosting bill was already $100/month.
The automation tax. Zapier charged per task. Make.com charged per operation. A single client signing a disclaimer through Pandadoc triggered a chain: Zapier catches the webhook, updates GoHighLevel, sends a Slack notification, logs to Google Sheets. Four "tasks" for one business event. Multiply that by every client, every action, every day. The automation bills climbed alongside the client count.
The deployment terror. Making any change to the WordPress installation affected every client simultaneously. Update a plugin? Every site gets it. Push a theme change? Every site shows it. There was no staging environment that accurately reflected the multisite network. I tested on a local single-site install and hoped for the best. Sometimes the best did not happen.
One Saturday morning I updated a plugin that conflicted with ACF field groups on three client sites. Course content disappeared from their dashboards. I spent the weekend rolling back database snapshots and manually verifying data integrity across sites. That was the weekend I started questioning whether WordPress was the right foundation.
The mobile wall. A client asked if their students could access courses from a phone app. I looked into it. Building a native mobile experience on top of WordPress meant either a janky WebView wrapper or a complete REST API layer that did not exist yet. The WordPress REST API existed in theory, but wiring it to ACF custom fields, WooCommerce orders, and multisite context was a project bigger than the original platform.
The "real developer" ceiling. I hit a wall I could not plugin my way past. Custom reporting across clients. Real-time notifications. Granular role-based access that went beyond WordPress's five built-in roles. Complex data relationships that ACF custom fields could not model cleanly. Every feature request pushed me closer to writing actual code — PHP functions, custom database queries, REST endpoints — and further from the drag-and-drop comfort zone.
The real cost
Here is what the V1 stack actually cost per month at its peak:
| Service | Monthly Cost |
|---|---|
| Managed WordPress hosting | $100 |
| Zapier (Professional) | $49 |
| Make.com (Core) | $29 |
| Pandadoc | $35 |
| GoHighLevel | $97 |
| Zoho CRM | $25 |
| Mailchimp | $20 |
| WooCommerce extensions | ~$15 |
| Total | ~$370/mo |
Nearly $400/month in platform costs before paying for a domain or my own time. And the platform was still slow. Clients noticed. I noticed. The gap between what the platform promised and what it delivered was growing.
The breaking point
The moment I decided to leave WordPress was not dramatic. It was a Tuesday afternoon. I was debugging a Zapier automation that had stopped syncing new WooCommerce orders to GoHighLevel. The Zapier task history showed the trigger fired correctly but the action failed with a 429 rate limit error from GoHighLevel's API. I needed to add retry logic.
Zapier's retry options were: wait and retry once, or upgrade to a more expensive plan for multi-step error handling. Make.com could handle it, but I would need to rebuild the entire automation chain in Make.com's visual editor. Neither option addressed the root problem — I was paying multiple services to do what a single backend with a job queue could do.
I opened a new browser tab and searched "learn React." That search changed the trajectory of my career.
The decision
I decided to rebuild the platform from scratch using modern web technologies. The plan was:
- Learn JavaScript/TypeScript properly
- Learn React and Next.js
- Understand relational databases beyond WordPress's
wp_poststable - Build a custom backend that owned its own data and integrations
- Migrate clients one at a time
It was terrifying. I had paying clients on a platform I was planning to replace. The new version would take months to build — months where I would need to maintain the WordPress version while learning an entirely new stack.
But the alternative was worse. Staying on WordPress meant every new feature request would be harder than the last. The platform would get slower as it grew. The third-party costs would keep climbing. And I would remain a "WordPress builder" in a world that was moving toward TypeScript, serverless, and real-time.
What carried forward
Not everything from V1 was thrown away. The product thinking survived intact:
- Plugin-based feature activation — the idea that clients should only see and pay for features they use came directly from watching clients struggle with WordPress's kitchen-sink approach
- Multi-tenant isolation — the need for each client to feel like they have their own platform, not a shared one
- Branded experiences — custom domains, logos, color schemes per client
- The integration mindset — understanding that a platform lives or dies by how well it connects to the tools clients already use
These concepts would shape every future version of Portal. The technology changed completely. The product vision stayed the same.
Next in the series: Portal V2: Learning to Build — Next.js, Drizzle, and the Long Migration — where I rebuilt everything from scratch, learned what a database index actually does, and discovered that moving off WordPress was only the beginning of the hard work.
Want to see how this was built?
See the Portal projectWant to see how this was built?
Browse all posts