[Blog](https://deploywithox.com/blog) [Guides](https://deploywithox.com/blog#c-guides) # How to self-host WordPress without Docker on your own VPS Run WordPress 7.1.3 on your own Ubuntu server with ox: FrankenPHP, MariaDB, a WP-Cron timer and an admin made on deploy, from the config that ran. Saurav Sharma Oct 17, 2026 10 min read On this page - [What you need](https://deploywithox.com/blog/self-host-wordpress#what-you-need) - [The steps](https://deploywithox.com/blog/self-host-wordpress#steps) - [The ox.toml, line by line](https://deploywithox.com/blog/self-host-wordpress#config) - [MariaDB and WP-Cron](https://deploywithox.com/blog/self-host-wordpress#services) - [Updates and backups](https://deploywithox.com/blog/self-host-wordpress#updates) - [Frequently asked questions](https://deploywithox.com/blog/self-host-wordpress#faq) To self-host WordPress you need an Ubuntu server, a fork of the WordPress repo with an `ox.toml` and two small PHP files added, and four variables. In our run the first deploy took about 2 minutes, and ox set up MariaDB for the site and a timer for WordPress's scheduled tasks. **How we tested** On Oct 17, 2026, ox deployed WordPress 7.1.3 from commit [`fc9832be`](https://github.com/WordPress/WordPress/commit/fc9832bef919c2a08541248db23af90d14ffb082) on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory. The run then redeployed it, backed it up and rebooted the server. Then a separate test agent, one that did not write the config, signed in, published a post and saw it on the blog's home page after a reload, and checked the app's services and its backup. [WordPress](https://github.com/WordPress/WordPress) is free publishing software for blogs and websites, written in PHP and released under the GPL, version 2 or later. You write in a block editor under `/wp-admin/`, and visitors read the front of the site. Content, users and settings live in a MySQL-compatible database, and uploaded files go to `wp-content/uploads`. The commit the run deployed is labelled `Tag 7.1.3` and says it was built from that tag in WordPress's own Subversion repository. ox deployed WordPress 7.1.3 (commit fc9832be) on Oct 17, 2026 on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory; the first deploy took 2 minutes and a redeploy took 31 seconds. - **First deploy:** 2 minutes, from adding the project until the app passed the run's first checks. - **Redeploy of the same commit:** 31 seconds, until the app passed the checks again. - **Server:** DigitalOcean, 8 vCPUs and 16 GB of memory (`s-8vcpu-16gb`), with this app alone on it. Every one of the 85 apps we deployed ran on this one size, so it was not picked for this app. - **Memory in use after the reboot:** 996 MB of 15986 MB, for the whole server: Ubuntu, the app and its staging copy, its services, Caddy and the ox agent. On a server with no apps, ox's tests on real servers hold the agent to 40 MB or less and Caddy to 50 MB or less. - **Services ox runs for it:** `mysql`. - **Scheduled jobs:** `wpcron`. - **Source:** [github.com/WordPress/WordPress](https://github.com/WordPress/WordPress) at 7.1.3, commit [`fc9832be`](https://github.com/WordPress/WordPress/commit/fc9832bef919c2a08541248db23af90d14ffb082). The new WordPress site's blog page The WordPress dashboard after logging in as admin The blog showing the post written in the browser ## What you need - An Ubuntu LTS server. The run used the size in the facts above and measured the memory there. WordPress's readme asks for PHP 7.4 and MySQL 5.5.5, each or newer, and names PHP 8.3 or newer and MySQL 8.0 or MariaDB 10.11 or newer as the versions it prefers. ox serves PHP apps with FrankenPHP, which carries PHP 8.5, and its mysql service is MariaDB 11.8. - A GitHub account to fork `WordPress/WordPress`, and an ox account, which you open by signing in with GitHub. - A site title, plus a name, an email address such as `admin@example.com` and a password for the first administrator. The run set up no mail, and sending mail was not tested. ## The steps Open Servers in the ox dashboard and press Add server. It shows a one-time install command. Run it as root on your Ubuntu server. It sets up the ox agent and Caddy, and the server then appears in your account. Fork `WordPress/WordPress` on GitHub and check out commit `fc9832be`, the one the run deployed. Add `ox.toml`, `wp-config.php` and `ox-install.php` at the root of your fork, as shown below, then commit and push. Next write a file named `wordpress.env` on your laptop with one `KEY=value` line for each of the four variables listed below. The config does not mark them as required, so fill in all four before the review. With the [ox CLI](https://deploywithox.com/docs/cli) signed in (`ox login`), run: Shell ```bash ox new /WordPress ox review wordpress --from-file wordpress.env --wait ``` ox lowercases the repo name for the project, so the project is `wordpress`. The review saves your values and starts the first deploy, and `--wait` follows the run until it ends. The console has the same two steps, and the [quickstart](https://deploywithox.com/docs/quickstart) walks through them. When the deploy is green, open the https address ox shows for the project and go to `/wp-login.php`. Sign in with `WP_ADMIN_USER` and `WP_ADMIN_PASSWORD`, and the dashboard opens at `/wp-admin/`. The first time the block editor opens, a welcome tip may need closing. To try it, open Posts, press Add New, write a line, and press Publish, then confirm: the post shows on the blog's home page. The run served WordPress on that preview address only, so a domain of your own was not part of it. The [domains page](https://deploywithox.com/docs/operate/domains) covers how ox adds one. ## The ox.toml, line by line TOML ```toml # WordPress 7.1.3 (PHP, MySQL), as its operator would write it: ox detects # FrankenPHP. The overlay adds wp-config.php (reads ox's MYSQL_URL) and an # installer that does what wp-cli's core install does. [build] # Creates the tables and the admin account on the first deploy, and runs the # database upgrade after a new release. frankenphp has PHP built in, so no # separate php-cli is needed. migrate = "frankenphp php-cli ox-install.php" [services] mysql = {} [storage] # Uploads are the only folder WordPress writes at run time. keep = ["wp-content/uploads"] [cron] # WP-Cron runs when a page loads, which a quiet site never does. wpcron = { schedule = "*/5 * * * *", run = "frankenphp php-cli wp-cron.php" } ``` There is no `[app]` section. ox finds `wp-load.php` and `wp-includes/version.php`, knows the repo is WordPress, and starts FrankenPHP with the repo root as the web root. - `migrate` runs `ox-install.php` on every deploy, after the build and before traffic switches. FrankenPHP has PHP built in, and `php-cli` is its way to run a script, so the server needs no PHP package of its own. - `mysql = {}` under `[services]` asks for a MariaDB that belongs to this project alone. It runs as a systemd unit under the project's user, with a password ox generates, and gives the app one variable, `MYSQL_URL`. - `keep = ["wp-content/uploads"]` names the one folder WordPress writes while it runs. A PHP release is read-only, so ox links that folder to the project's data directory, which stays writable and survives deploys. - The `wpcron` job runs `wp-cron.php` every five minutes, in UTC. WordPress's header in that file says it normally runs when someone visits, and that a site with few visits can call it from a server cron. A quiet site gets no visits, so the timer keeps scheduled tasks running. `ox-install.php` Code ```