«When someone tells me that WordPress is only for websites, I show them my CI/CD»
WordPress as CI/CD: yes, I’m serious
Late 2014, winter. I came as a WordPress developer to a volunteer project — and there I first encountered the use of Beanstalk. Push to repo — files on the server. No FTP, no manual copying. For me back then, it was magic.
From that moment on, Beanstalk became part of my workflow. More than ten years — without complaints. It just worked.
But when the number of projects grew — client sites, my own products, pet projects — I started counting. Another monthly subscription to an external service — all for one simple operation: transfer files from the repository to the hosting. It’s like keeping a full-time courier who moves one box across the street every day.
This made me look at WordPress differently. Essentially, everything needed to build my own Beanstalk — already exists in WordPress.
Why “proper” CI/CD tools don’t fit
The first thing any DevOps engineer will say: “GitHub Actions + Docker + rsync”. A beautiful scheme. On paper.
Now reality: shared hosting. Hosting Ukraine. There’s no Docker here. No way to install packages. No systemd for services. SSH exists, but it’s limited — no root. GitHub Actions can build a project, but how do you deliver files to the server without full rsync or SCP?
Jenkins? A separate server that needs to be rented, configured, and maintained. Deployer? Requires SSH with keys and a full CLI environment. All of this — additional infrastructure and additional costs for one task.
I could have gotten a VPS for CI/CD. But that’s like renting a garage to store one wrench.
WordPress — not a CMS, but an application framework
This idea took shape over time. But when you’ve been working with WordPress for seventeen years — you start seeing it not as a CMS for websites, but as a platform with a full set of tools:
- Custom Post Types — data structures for anything
- ACF PRO — building interfaces without writing HTML forms
- REST API — integration with any external service
- WP-Cron — deferred and regular tasks
- Admin panel — ready UI with authorization, search, filtering
The idea crystallized simply: every deploy is a record in WordPress. Custom Post Type with fields: from where (repository + branch), to where (site on hosting). Create a record — the system automatically registers a webhook on GitHub. Push — files on the server. Delete the record — webhook disappears. Zero manual work.
The system is connected via API to GitHub and Hosting Ukraine — repositories, branches and sites are pulled automatically.
What’s under the hood
The stack is minimal, but each element is in its place:
- WordPress — system core, data storage, UI
- ACF PRO + Timber/Twig — custom fields and admin panel templates
- GitHub API — webhook management, list of repositories and branches
- Hosting Ukraine API (adm.tools) — list of sites with paths to directories
- Telegram Bot API — deploy status notifications
Two custom ACF fields that solve UX
The most interesting part — two field types written from scratch for ACF.
GitHub Repository — cascading select. The first list pulls repositories of the authorized user through the GitHub API with 15-minute caching. Select a repository — the second list loads branches via AJAX, 10-minute cache. No manual input, no naming errors.
Hosting Ukraine Sites — similar logic for hosting. Domain → subdomain. Each option automatically stores the path to the site’s home directory on the server. All through the adm.tools API.
Create a deploy — just select from the lists. Like a constructor: from where → to where. Two selects on the left, two on the right. Done.
One push — code on the server
When saving a record, the system generates a unique token — an encrypted ID via AES-256-CBC — and forms the webhook URL. This URL is automatically registered on GitHub. When you delete the record, the webhook is deleted. Everything automatically.
When GitHub sends a push event, a chain begins:
- Token decryption, configuration search
- Message in Telegram: “Deploy started”
- Download ZIP archive from GitHub repository (authorization via Personal Access Token)
- Unpacking, clearing the target directory, moving files
- Deleting unnecessary files:
.md,.gitignore,prepros.config - Message in Telegram: “Done” or “Error”
For webhook deploys, the process runs asynchronously — PHP script in the background via CLI. GitHub gets a response immediately, doesn’t wait for completion. For manual deploys via a button in the admin — synchronous mode with the result straight in the browser.
Push from your phone, message in Telegram within a minute — code is already on the server. This is the level of automation it was all built for.
Bonuses I didn’t plan
When you have a working infrastructure with UI and a list of all sites — the next ideas come naturally.
HTTP Authentication manager
The second Custom Post Type — “Authentication”. Select a site from the list, enter login and password — the system generates .htpasswd and adds the appropriate block in .htaccess. Delete the record — protection is removed automatically.
Close staging with a password, open it for the client — two clicks instead of an SSH session. There’s even an API endpoint for getting authorization data — I use it in my own Chrome extension to open password-protected sites in one click. But I’ll tell you about that later — it’s a separate big and very interesting topic.
Honest about limitations
The system isn’t perfect. I know this and I’m not pretending it’s an enterprise solution.
- No rollback — broke the deploy — either push a fix or restore from hosting backup. Storing the previous version before clearing — on the roadmap, but in half a year of operation, the need never came up
- ZIP instead of git clone — each deploy downloads the full repository archive. For WordPress themes and plugins (a few megabytes) this is non-critical, but for a large monorepo it would be inefficient
- WordPress as a single point — if WordPress on this server goes down, the deploy goes down too. Hasn’t happened in six months, but the risk exists
- One hosting provider — integration with Hosting Ukraine API. For another provider, it would need to be adapted
This is a tool built for the specific conditions of a specific developer. In those conditions — thirty-plus projects on shared hosting — it works flawlessly.
Summary
More than thirty deploy configurations, client projects and pet projects — everything runs through one WordPress. Not a single failure in six months. Not a single unnecessary subscription.
The main takeaway: the best solution isn’t the one recommended at conferences. It’s the one built for your actual needs, using tools you master virtuosically.
If you have a business and feel like your infrastructure is held together with tape and manual work — write to me. I build things that work. For your tasks, your stack and your needs.