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:

  1. The WordPress Application: This includes the PHP code, themes, and plugins that make up your site.
  2. 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.

  1. 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.
  2. 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.

  3. Configure fly.toml: The fly launch command created a fly.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.
  4. 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>'
    
  5. 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.

  1. Create a Project Directory: In a new directory on your local machine, we'll set up the files for our WordPress app.

  2. The Dockerfile: Create a file named Dockerfile. 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 and uploads.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
    
  3. 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
    
  4. 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
    
  5. Configure fly.toml: The fly.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 and WORDPRESS_DB_USER must match the values you set in the database's [env] section.
  6. 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>'
    
  7. 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.

0
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.