← All work
Case studySaaS · Cloud marketplace · Server automation

WordPress Hosting Control Panel

A self-serve SaaS for running WordPress on your own cloud. A customer connects a cloud account; the panel creates the server, installs a tuned LEMP stack and a management agent, and from then on runs the site, SSL, PHP, database, cache, backups, files, SSH and WordPress itself from the browser — with Stripe billing on top.

I built it alone over two years and three generations — from a central API for Google Cloud servers in 2023 to a platform that provisions on five clouds — plus a single-server edition sold as a licensed Google Cloud Marketplace image.

Role
Sole engineer — design, engineering & coding
Editions
Multi-cloud SaaS · Marketplace image
Timeline
Apr 2023 – Feb 2025 · 3 generations
Status
v1 shipped February 2025
The problem
Customers want WordPress on their own cloud account — their server, their bill — without learning to run a Linux server.
What was built
Morsalin, as sole engineer, built a panel that provisions servers on five clouds and runs them through a signed Python agent, plus a Google Cloud Marketplace edition.
The result
v1 shipped February 2025. Sites, SSL, PHP, databases, backups and WordPress itself are run from the browser, with Stripe billing on top.
~29.8klines of PHP
~42.2klines of Vue
~1.2klines of Python agent
~3klines of JS, CSS & server templates
5cloud providers
~19klines of PHP in generation 1
74pages
~270routes
3generations
2editions
~22klines in the Marketplace edition
5 minsigned installer links
01The problem

Running WordPress well means running a server well.

01

A managed host owns your infrastructure.

Convenient, but the server, the network and the bill belong to someone else.

02

A bare VM owns your weekend.

nginx, PHP pools, databases, certificates, backups and updates all have to be learned, configured and kept running.

03

The middle path.

The customer keeps the cloud account and the bill. The panel does the operations — provisioning, tuning, certificates, backups, updates and troubleshooting — all from a browser.

02What it does

Everything a WordPress server needs, in a browser.

Provisioning

Creates an Ubuntu server on five clouds, with regions, sizes and plans fetched live from each provider.

Web & PHP

nginx with HTTP/2 and HTTP/3, security and tuning; PHP 8.1 to 8.4 with FPM pool tuning, logs and metrics.

Data

MariaDB databases, users and grants with phpMyAdmin single sign-on; Redis configuration.

SSL

Let’s Encrypt over HTTP or DNS through five DNS providers, uploaded certificates, or the panel’s own authority.

Backups

Encrypted, deduplicated snapshots to eight storage providers, on schedules from hourly to monthly, with restore.

WordPress

Install, updates, themes, plugins, cron, maintenance mode, magic login, database tools and migration from other hosts.

Files & services

A file manager with an in-browser code editor, SSH keys, systemd services, processes and live stats.

Self-healing

About thirty health checks, each with a one-click fix — including configuration drift.

Billing

Stripe subscriptions with plan-based site limits, invoices, tax IDs and the billing portal.

03Architecture

One site, one server, one agent.

fig.02 — system architecturepanel · cloud · agent
Customerbrowser · no terminalControl panelLaravel 11 · Vue 3 · Inertiaencrypted cloud credentialsvalidation · reconciliation · schedulesStripe billinglive updates on private channelscreate VM5 cloud provider APIsCompute Engine · Lightsail · DigitalOceanLinode · Vultr — in the customer's accountVM + firewallCUSTOMER'S SERVER · UBUNTUSigned installer5-minute linkretries until it succeedsPython agenttokens bound to this serverdrops to the site's user› nginx + HTTP/3› PHP 8.1–8.4 FPM› MariaDB + phpMyAdmin› Redis› restic backups› wp-clifirewall: the agent port answers only the panel~30 health checks · configuration-drift detectionHTTPS · short-lived signed tokens8 backup storage providers

One site, one server

The first design modelled servers, domains and sites separately, like a traditional panel. Halfway through, I collapsed them: a site is a server. It removed a layer of indirection from every screen and command, made isolation physical rather than configured, and let one model own the whole stack.

Configuration as templates

nginx, PHP pools, MariaDB, Redis, logrotate, phpMyAdmin, backup remotes and the install scripts are all rendered per site. The same templates make drift detection possible — and every fix is just a re-render.

04The server agent

An agent, not SSH.

The panel never opens a shell on a customer’s server. Each server runs a small agent I wrote in Python — Flask on gunicorn, run by systemd — and the panel talks to it over HTTPS once DNS and the certificate verify. In production, the agent’s port answers only the panel’s address, enforced by the cloud firewall on all five providers.

01

Tokens bound to one machine

Every request carries a token that expires within the hour. The agent verifies its signature against the signing key’s published certificates, then checks that its audience is a hash of the server’s own public address — so a token issued for one machine is refused by every other.

02

Privilege drops per request

File-manager and SSH-key operations switch to the site’s own Linux user before touching the disk, so the agent’s root access is never used on a customer’s files.

03

A deliberately small surface

Eight modules — processes, Linux users, SSH keys, file manager, file system, MariaDB, stats and systemd — and nothing else. Run by systemd, restarted automatically.

04

Shipped as native code

The Python modules are compiled with Cython into native extensions and installed as an operating-system package from a signed repository.

05Self-installing servers

A server that installs itself.

  1. 1

    The cloud creates the VM

    With first-boot data in the format each provider expects.

  2. 2

    First boot fetches the installer

    It checks it is root, exits early if a previous install finished, and fetches the second stage from a signed URL that expires in five minutes.

  3. 3

    The stack installs

    nginx, MariaDB, Redis, PHP 8.1–8.4, backup and WordPress tooling and the agent, from a signed package repository — with swap on small machines and only the needed ports opened.

  4. 4

    A retry that knows when to stop

    A systemd timer re-runs the installer until the agent is present, and does nothing after that.

  5. 5

    The panel connects

    A scheduled job polls until the agent answers, then turns the deployment into a live site — or gives up cleanly after two hours.

Every step checks before it acts, so running any part twice is harmless.
06Commands that succeed

A command only reaches a server when it can succeed.

Validation first

182 request classes validate every input — domains, directory names, plugin slugs, database identifiers — before anything reaches a server.

Preconditions as middleware

28 middleware say when an action is allowed: site online, WordPress installed, certificate type, plan limit. Each has a page guard and an API guard, so the UI never offers what the API would refuse.

Reconciliation on change

Model observers turn edits into server changes. A new hostname reissues the certificate and rewrites nginx; a new alias re-renders exactly the files it affects and restarts only what changed.

Drift detection

Every configuration file is a template, so the panel can compare what is on a server with what it would write — and fix the difference.

07Security

Least privilege, all the way down.

Encrypted credentials

Cloud and DNS keys are encrypted at rest and never serialised; a credential cannot be deleted while a site depends on it.

Least privilege

Commands, files, wp-cli and backups run as the site’s own Linux user; the default PHP pool disables process and socket functions.

Arguments, not shell strings

Commands built from user input — installing a plugin, generating a login link, checking DNS — are passed as argument lists.

SQL by the query builder

Server-side database administration is built and escaped by the framework’s query builder, with system databases protected.

Signed, expiring links

For the installer, email verification, administrator access and phpMyAdmin single sign-on (encrypt-then-MAC, five-minute expiry).

A private certificate authority

Issues 4096-bit certificates covering every domain and alias, with serial numbers generated to the standard’s rules.

08Backups & WordPress

The work a site owner never wants to do.

Backups

Encrypted, deduplicated restic snapshots shipped through rclone to AWS, Cloudflare, DigitalOcean, Linode, Wasabi, Vultr, Alibaba or Google Cloud Storage. Twelve cadences fan out creation, archiving and pruning, and unique job keys stop two snapshots of one site from overlapping.

WordPress

Everything through wp-cli as the site user: install, core, themes and plugins with auto-update, options, cache, cron run by the panel on schedule, maintenance mode, salts, magic login links and a full set of database tools.

Live interface

Every change broadcasts over private, ownership-checked channels, so pages update as servers come online. nginx and PHP-FPM metrics are collected hourly, and files open in an in-browser Monaco editor.

09Marketplace edition

The same idea, sold as a licensed server image.

A single-server edition for Google Cloud Marketplace: two more applications — a small central licensing and installer service, and a panel that runs on the customer’s own VM. Launch the image, and it installs, licenses and configures itself with no one in the loop.

fig.03 — from marketplace to running serverlicence · install · panel
Marketplace imagelaunched by the customerFirst bootreads licences from metadataLicensing & installer service› hands out a signed installer link, valid 5 minutes› checks the VM's licence codes against published images› images, regions and zones synced from the Compute API› records every licensed instanceSigned private package repositorynginx · MariaDB · Redis · PHP 8.1–8.3 · Caddy · the panelInstallersafe to run againTHE CUSTOMER'S VMPanel setup› root and licence required› breach-checked password› DNS must point at this VM› hostname certificateControl panel› Laravel · Vue 3 · Inertia› served by Caddy over TLS› configs rendered from templates› WordPress cron on a scheduleSites, each isolated› own Linux user and PHP-FPM pool, open_basedir› nginx · PHP 8.1–8.3 · MariaDB · Redis› Let's Encrypt or self-signed SSL · phpMyAdmin› WordPress toolkit via wp-cli, as the site user

Only licensed machines install

First boot receives a signed installer link valid for five minutes. The VM posts its licence codes from the cloud’s metadata server, and every one must belong to a published image — images, regions and zones synced from the Compute Engine API, not typed in.

An installer that is safe to re-run

It adds the signed package repository, installs and verifies each package, prepares nginx, enables services, and adds swap on small machines only if none exists.

Setup checked at every step

Root and a valid licence required; a password checked against known breaches; the hostname’s DNS must point at the VM before a certificate is requested; the panel is then served by Caddy over TLS.

Every site in its own sandbox

Each site runs under its own Linux user and PHP-FPM pool with open_basedir and disabled process functions; wp-cli and composer run as that user, and passwords reach wp-cli and MySQL over standard input.

January – February 2024

95 commands

It started as a command-line tool — sites, databases, users, certificates and WordPress, each a short shell command — then the logic moved into reusable libraries and the machine learned to read its own cloud metadata.

March – July 2024

A web panel

A JSON API and a Vue interface replaced the commands over the same libraries: 23 pages, 111 routes and 50 validated request types, with one setup command left for the admin account, hostname and certificate.

10How it evolved

Two years, three generations.

  1. Apr 2023Generation 1

    A central Laravel API managing WordPress servers launched from Google Cloud images, talking to an agent on each server over HTTP.

  2. Jun–Sep 2023

    A server onboarding pipeline — instance metadata, hostname DNS, hostname SSL, web server preparation and agent handshake — plus an admin for images, regions and zones synced from the Compute Engine API; the interface moved to its own TypeScript app.

  3. Jan–Jul 2024Marketplace edition

    A second edition branched off: a single-server panel sold as a licensed Google Cloud Marketplace image — first as a 95-command CLI, then as a web panel.

    The Marketplace edition ↑
  4. Feb 2024Generation 2

    A fresh Laravel 11 codebase — servers, sites, domains and certificates.

  5. Apr 2024

    First large build; teams removed within the month to keep the model simple.

  6. May–Jul 2024

    Unified encrypted credentials, WordPress toolkit, Stripe billing, policies, nginx and PHP metrics.

  7. Oct 2024Generation 3

    Servers, domains and sites collapsed into one site-per-server model, and the agent rebuilt around it — the largest rewrite of the project.

  8. Nov 2024

    Self-installing servers, certificate provisioners, restic backups and scheduled cadences.

  9. Dec 2024

    From “bring your own server” to “create one in your cloud”, on five providers; phpMyAdmin single sign-on.

  10. Jan–Feb 2025

    Cloud-init installer, site migration, support — and the v1 release.

Each generation kept what worked — the agent, the templates, the WordPress toolkit — and made the model simpler: a fresh codebase in 2024, teams dropped early, then servers, domains and sites collapsed into one. Every simplification made later features cheaper to build.

11Key decisions

Every non-obvious choice, with its reason.

One site per server

Isolation becomes physical, and one model owns the whole stack.

An agent over HTTPS, not SSH

No shell on customer servers; access is scoped, signed and short-lived.

The agent checks its own address

A token only works on the machine it was issued for, verified on that machine.

Signed, five-minute installer URL

A copied install command stops working almost immediately.

Check before every install step

Re-running provisioning is always safe.

Validation and precondition middleware

A command only reaches a server when it can succeed.

Templates for every config file

The same source renders, audits and repairs a server.

Run as the site’s Linux user

A compromised site cannot reach the rest of the machine.

Enums generated into the frontend

Backend and UI can never disagree on a value.

Drop teams early

A simpler model made every later feature cheaper to build.

Licence by image licence codes

It is how the marketplace marks paid VM images, and it needs no customer credentials.

Passwords over standard input

Nothing sensitive in process lists or shell history.

12Outcome

Hosting without a terminal.

Provisioning

A server created in the customer’s own cloud on five providers, installing and connecting itself.

Operations

nginx, PHP, MariaDB, Redis, services, files and SSH managed entirely from the browser.

Resilience

Scheduled encrypted backups to eight providers, and self-healing checks with one-click fixes.

Business

Stripe subscriptions with plan-based limits, and a licensed Marketplace edition for single servers.

  • Hosting without a terminal — every routine operation a WordPress site needs is a button, a form or a schedule.
  • Customers keep their infrastructure — the server and the bill stay in their own cloud account.
  • Pages update live as servers come online and jobs finish, over private, ownership-checked channels.
  • Built end to end by one engineer, from the install script on a fresh VM to the last page of the billing portal.

My role

Everything, alone, across both editions: the product itself, the data model, the provisioning flow for five clouds, the install scripts and server configuration templates, the on-server agent and its protocol, the Marketplace licensing service and single-server panel, the security model, billing, and every page. I designed, built and coded it, front to back.

Stack

Backend
PHP 8.3 · Laravel 11 · Sanctum · Cashier · Socialite · Reverb
Frontend
Vue 3 · Inertia · Pinia · Tailwind · Monaco editor
Agent
Python · Flask · gunicorn · Cython · OS package
Clouds
Compute Engine · Lightsail · DigitalOcean · Linode · Vultr
Servers
Ubuntu · nginx HTTP/3 · PHP-FPM · MariaDB · Redis
Ops
Certbot · private CA · restic · rclone · wp-cli · cloud-init
PHPLaravelVue 3InertiaPythonCloud provisioningMulti-cloud APIsCloud MarketplaceLicensingLinux automationnginxPHP-FPMMariaDBTLS & PKIBackups (restic)WordPressStripe billingReal-time UISaaS architecture

Described generically — product, company and infrastructure identifiers are deliberately omitted.

Next

Building a platform that runs other people’s servers?

Get in touch →
© 2026 MD Morsalinbuilt with care · OSS-first · sustainable by design
lat: 23.81°Nlon: 90.41°Etz: UTC+6status: 200 OK