Free WordPress Hosting: A Step-by-Step Guide to Deploying with Docker and Fly.io

Are you tired of paying hefty monthly fees for managed WordPress hosting? Do you want more control over your website's environment? If so, you're in the right place. This guide will walk you through setting up a powerful, scalable, and incredibly cost-effective WordPress site using Docker and the cloud platform Fly.io.
For many, "self-hosting" sounds intimidating, bringing to mind complex server management and late-night troubleshooting. However, by containerizing our application with Docker, we can package everything WordPress needs into a neat, reproducible box. And by deploying it on a platform like Fly.io, we get the benefits of a modern cloud infrastructure—like global distribution and easy scaling—often for a fraction of the cost of traditional hosting. In fact, with Fly.io's generous free tier, your hosting could be completely free.
This guide is based on the Free Tribe Network's own open-source setup. Let's get started.
What You'll Need
- A Fly.io account.
flyctl
, the Fly.io command-line tool, installed.- Docker Desktop installed and running.
- Basic familiarity with your computer's terminal (command line).
The Two-Part Harmony: WordPress + Database
A standard WordPress installation has two main components:
- The WordPress Application: This includes the PHP code, themes, and plugins that make up your site.
- The Database: This is where all your content—posts, pages, user information, and settings—is stored.
We will deploy these as two separate, but connected, applications on Fly.io. This is a best practice that makes the system more robust and easier to manage.
Part 1: The Database Backbone (MySQL)
First, we'll set up our MySQL database.
Launch the Database App: We'll start by telling Fly.io to create a new app using the official
mysql:8
Docker image.fly launch --no-deploy --image mysql:8 --name your-app-name-db
--no-deploy
: We don't want to deploy it just yet; we have some configuration to do first.--name
: Give your database a unique name. It's good practice to append-db
to it.
Create a Persistent Volume: By default, a Fly.io app's filesystem is ephemeral. To store our database data permanently, we need to attach a volume.
fly volumes create mysqldata --size 10
This creates a 10GB persistent volume named
mysqldata
.Configure
fly.toml
: Thefly launch
command created afly.toml
file. Open it and ensure it contains the following, paying close attention to the[mounts]
and[env]
sections.app = 'your-app-name-db' primary_region = 'jnb' # Or your preferred region [build] image = 'mysql:8' [mounts] source = "mysqldata" destination = "/var/lib/mysql" # The standard location for MySQL data [env] MYSQL_DATABASE = "wordpress" MYSQL_USER = "wp_user"
- The
[mounts]
section tells Fly to mount the volume we created to the correct directory inside the container. - The
[env]
section sets up the initial database name and user.
- The
Set Your Secrets: We need to set a password for the database user and the root user. These are stored as encrypted secrets in Fly.io, not in the
fly.toml
file.fly secrets set MYSQL_PASSWORD='<choose-a-strong-password>' MYSQL_ROOT_PASSWORD='<choose-a-stronger-password>'
Deploy! Now, we're ready to deploy our database.
fly deploy
Part 2: The WordPress Frontend
With the database running, we can now set up the main WordPress application.
Create a Project Directory: In a new directory on your local machine, we'll set up the files for our WordPress app.
The
Dockerfile
: Create a file namedDockerfile
. This file defines our custom WordPress container. We'll use the official WordPress image as a base and add a few tweaks.FROM wordpress:latest # Add CORS configuration for headless/API access COPY cors-config.php /usr/src/wordpress/wp-content/mu-plugins/cors-config.php # Increase default file upload limits COPY uploads.ini /usr/local/etc/php/conf.d/uploads.ini
This setup includes configurations for CORS (useful for headless setups) and increases the file upload size. You'll need to create the
cors-config.php
anduploads.ini
files with your desired settings.Here's mine:
<!-- cors-config.php --> <?php /** * Plugin Name: REST API CORS * Description: Enable CORS for REST API * Version: 1.0 * Author: Free Tribe Network */ add_action('rest_api_init', function() { remove_filter('rest_pre_serve_request', 'rest_send_cors_headers'); add_filter('rest_pre_serve_request', function($value) { header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE'); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Expose-Headers: Link'); header('Access-Control-Allow-Headers: X-WP-Nonce, Content-Type, Authorization'); return $value; }); }, 15);
; uploads.ini file_uploads = On memory_limit = 256M upload_max_filesize = 64M post_max_size = 64M
Launch the WordPress App: Just like the database, we'll launch a new Fly.io app.
fly launch --name your-app-name-wp --no-deploy
Create a Volume for
wp-content
: We need another persistent volume to store theme and plugin files, and user uploads.fly volumes create wp_content --size 10
Configure
fly.toml
: Thefly.toml
for our WordPress app is crucial. It's how we connect to our database.app = 'your-app-name-wp' primary_region = 'lhr' # Match your database region if desired [build] dockerfile = "Dockerfile" [mounts] source="wp_content" destination="/var/www/html/wp-content" [env] WORDPRESS_DB_HOST = "your-app-name-db.internal:3306" WORDPRESS_DB_NAME = "wordpress" WORDPRESS_DB_USER = "wp_user"
WORDPRESS_DB_HOST
: This is the key. It points to the internal network address of our database app. The.internal
address is a private network address that only your Fly.io apps can access.WORDPRESS_DB_NAME
andWORDPRESS_DB_USER
must match the values you set in the database's[env]
section.
Set the Database Password Secret: We'll set the database password as a secret, just like we did for the database app itself. This must be the same password.
fly secrets set WORDPRESS_DB_PASSWORD='<the-same-strong-password-from-before>'
Deploy WordPress!
fly deploy
Conclusion: Freedom and Control
That's it! You now have a fully functional, high-performance WordPress site running on a modern cloud platform. You can access it via https://your-app-name-wp.fly.dev
.
By moving away from traditional, restrictive hosting, you've not only unlocked significant cost savings but also gained a new level of control. You can now easily scale your resources, deploy changes with a simple fly deploy
, and rest easy knowing your application is packaged neatly in a container, ready to be hosted anywhere. Welcome to the future of WordPress hosting.
Subscribe to my newsletter
Read articles from Jedidiah Amaraegbu directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Jedidiah Amaraegbu
Jedidiah Amaraegbu
Full Stack Software Engineer (React, Node) with 5+ years of experience collaborating in cross-functional teams to deliver scalable solutions that exceed expectations, drive business growth, streamline workflows, and increase user engagement. Proven track record of mentoring 200+ developers across 3 continents and implementing enterprise payment solutions. Stripe Certified Developer specializing in clean architecture and modern tech stacks.