Blog Logo

Jim Hope

Installing WordPress on Docker

Author

Jim

Date Published

Moving Away from WordPress Multisite: My Journey to Isolated Docker Containers

Over the last couple of weeks, I’ve noticed a few issues crop up on the WordPress sites that I manage for myself, friends, and family. Running them all from a single WordPress Multisite instance meant that if something went wrong on one site, it would cascade and break all the others. I found myself having to restart the entire cloud-hosted virtual machine each time just to figure out what was wrong.

After three days of dealing with this headache, I reached my breaking point. I decided to back up all of the content, set up a new Docker host, and launch individual Apache/WordPress environments for each site.

As it turns out, this was much easier said than done.

The Backup Strategy

I started by backing up each individual site using the Prime Mover plugin. I chose this specific tool because it can back up a Multisite installation locally without relying on external cloud services that have strict storage limits. Crucially, it also features the ability to export a single sub-site out of a Multisite network as an independent, standalone backup.

Once the exports were safely downloaded, the real work began: building an isolated web server infrastructure for each website.

Facing the Docker Dilemma

My original plan was simple: deploy the official WordPress Docker image alongside a separate database container for each site. While the initial spins went smoothly, the roadblocks appeared when I attempted to restore the Prime Mover backups.

It turns out that the Prime Mover plugin requires specific PHP extensions that aren't included in the official, stripped-down WordPress Docker image. Try as I might, I couldn't get the required modules to compile or install cleanly inside the official runtime container.

To get around this, I pivoted. I launched a clean Ubuntu base image, gathered a comprehensive list of every PHP module required or recommended for both WordPress and Prime Mover, and built the environment from scratch.

This time, the restore worked flawlessly! Or so I thought...

Troubleshooting the WordPress REST API & SSL

The moment I sat down to write a draft post explaining everything that had happened, I realized the draft wouldn't save. The block editor threw an error because the WordPress REST API was failing.

After what felt like an eternity searching through forum threads, I discovered three separate configuration oversights:

  1. I hadn't added the specific environment flag in my wp-config.php file to notify WordPress that it was running behind an SSL proxy.
  2. I had forgotten to enable Apache's native mod_rewrite module.
  3. I hadn't updated the directory permissions in the apache2.conf file to honor .htaccess overrides.

After tracking down and fixing those three issues, everything finally hummed to life. A fair bit of copying, pasting, and database pairing later, I now have all of my sites running in their own isolated Docker containers, with Cloudflare Tunnels securely routing web traffic from the edge straight to the server.

It might not be the most glamorous project, but I wanted to share the exact configuration that finally got everything running correctly in a standard Ubuntu container. Consider this post both a helpful guide for anyone hitting the same wall, and a personal backup for my future self!

The Deployment Blueprint & Commands

Here are the step-by-step terminal commands and configuration blocks used to build out the environment.

1. Install System Dependencies & PHP Modules

First, update the package repository, upgrade existing packages, and install Apache along with all the core, caching, and image-processing extensions WordPress requires:

Bash

apt update && apt upgrade -y && apt install -y nano curl wget zip unzip apache2 php php-json php-mysqli php-curl php-dom php-exif php-fileinfo php-igbinary php-imagick php-intl php-mbstring php-xml php-zip php-apcu php-memcached php-opcache php-redis php-iconv php-shmop php-simplexml php-xmlreader php-ssh2 php-ftp php-sockets

2. Download and Extract WordPress

Clean out the default Apache placeholder directory and pull down the latest clean zip directly from WordPress:

Bash

# Move into the server's web directory
cd /var/www

# Remove the default HTML folder and its content
rm -rf html

# Download the latest version of WordPress
wget https://wordpress.org/latest.zip

# Unzip the archive
unzip latest.zip

# Rename the unzipped folder to match Apache's standard root
mv wordpress/ html/

# Recursively set correct permissions for the web server
chmod 755 -R /var/www/html
chown -R www-data:www-data /var/www/html

3. Initialize the Apache Service

Note: Because standard base Ubuntu Docker images lack systemd (systemctl), you will need to start the service manually using the legacy service command if your container restarts.

Bash

service apache2 start

4. Database Setup & wp-config.php Tweaks

Navigate to your container's web address to begin the WordPress setup wizard. Connect it to your database. (If you are running MySQL/MariaDB in a separate container, ensure both containers share the same Docker bridge network, and use the database container's internal Docker IP or container name as the Database Host).

Stop right before running the final installation script so we can patch the config file:

Bash

nano /var/www/html/wp-config.php

Add the following lines near the top of the file to force SSL detection through your proxy/tunnel and bypass FTP prompts during plugin updates:

PHP

// Force WordPress to recognize HTTPS behind a reverse proxy or Cloudflare Tunnel
$_SERVER['HTTPS'] = 'on';

// Allow direct plugin and theme installations without forcing FTP setup
define( 'FS_METHOD', 'direct' );

5. Tune PHP Upload Limits via .htaccess

Create or edit the .htaccess file to increase file execution times and maximum upload thresholds for large backup restores:

Bash

nano /var/www/html/.htaccess

Paste in these PHP configuration overrides:

Plaintext

php_value upload_max_filesize 64M
php_value post_max_size 128M
php_value memory_limit 256M
php_value max_execution_time 300
php_value max_input_time 300

6. Configure Apache for URL Rewriting

To fix broken permalinks and API routing errors, Apache needs to be told to respect .htaccess directives. Open the primary config file:

Bash

nano /etc/apache2/apache2.conf

Locate the block targeting /var/www/ and change AllowOverride None to AllowOverride All:

Plaintext

<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

7. Enable the Apache Rewrite Module & Restart

Commit the changes by activating the rewrite module and bouncing the web server:

Bash

# Enable the Apache Rewrite module
a2enmod rewrite

# Restart Apache to apply all changes
service apache2 restart

With all of that securely in place, you can head back to the browser, finish the installation wizard, and safely restore your site without hitting a single wall!

Comments

No comments yet. Be the first to share your thoughts!