From VMs to Containers – Deploying WordPress the Modern Way

From VMs to Containers – Deploying WordPress the Modern Way

In a previous blog, we talked about the benefits of leveraging containerization in software deployment. In this blog, we wanted to give a you a technical walkthrough of how much easier the process gets when you adopt containerization. We will use Wordpress for our example.

WordPress powers nearly half of all websites on the internet, but how we deploy it has evolved dramatically over the years. This evolution mirrors the broader DevOps maturity journey many organizations experience – from manual configuration to infrastructure-as-code. Let's explore three approaches to WordPress deployment, examining how each improves automation, reproducibility, and operational efficiency while reducing those late-night "why isn't this working" moments.

Phase 1 – Traditional Deployment on a Virtual Machine

What is a Virtual Machine?

Virtual machines are essentially computers within computers-complete operating systems running on virtualized hardware. They provide excellent isolation and compatibility but come with significant overhead.

Pros of VM-based deployments:

  • Complete isolation from the host and other VMs
  • Full OS capabilities and traditional tooling
  • Familiar system administration workflows

Cons of VM-based deployments:

  • Resource-intensive (each VM needs its own OS)
  • Slower startup times and larger disk footprints
  • Manual, error-prone setup processes

Step-by-Step Deployment

Let's walk through installing WordPress on an Ubuntu VM:

# 1. Update system packages
sudo apt update && sudo apt upgrade -y

# 2. Install Apache web server
sudo apt install apache2 -y
sudo systemctl start apache2
sudo systemctl enable apache2

# 3. Install MySQL
sudo apt install mysql-server -y
sudo systemctl start mysql
sudo systemctl enable mysql

# 4. Secure MySQL installation
sudo mysql_secure_installation

# 5. Create WordPress database
sudo mysql
CREATE DATABASE wordpress;
CREATE USER 'wordpressuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpressuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
# 6. Install PHP and required extensions
sudo apt install php libapache2-mod-php php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip -y

# 7. Download and set up WordPress
cd /var/www/html
sudo wget https://wordpress.org/latest.tar.gz
sudo tar -xzf latest.tar.gz
sudo rm latest.tar.gz

# 8. Set proper permissions
sudo chown -R www-data:www-data /var/www/html/wordpress
sudo chmod -R 755 /var/www/html/wordpress

Setting up the Apache virtual host:

sudo nano /etc/apache2/sites-available/wordpress.conf

    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/wordpress
    ServerName your-domain.com

    
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

# Enable the site and restart Apache
sudo a2ensite wordpress.conf
sudo a2enmod rewrite
sudo systemctl restart apache2

Finally, navigate to your server's IP address or domain in a browser to complete the WordPress setup wizard.

Challenges

This traditional approach presents several drawbacks:

  • Manual effort: The process involves numerous steps, each with potential for error.
  • Reproducibility issues: Ever tried setting up two identical WordPress installations? Good luck with that.
  • Configuration drift: Over time, servers become unique snowflakes that no one wants to touch.
  • Scalability problems: Need another server? Hope you documented all those custom tweaks!

This method works, but it's like driving a stick shift uphill in traffic. It gets the job done, but there's a lot of unnecessary clutch work involved.

Phase 2 – Deploying WordPress with Docker CLI

Intro to Docker

Docker revolutionized deployment by introducing lightweight containers that share the host system's kernel while providing isolated environments. Unlike VMs, containers package just the application and its dependencies-not an entire OS.

Key Docker Concepts for Beginners

  • Images: Read-only templates containing an application and its dependencies
  • Containers: Running instances of Docker images
  • Volumes: Persistent data storage that exists independently of containers
  • Networks: Virtual networks allowing containers to communicate with each other

Hands-On Deployment

Let's deploy WordPress using Docker CLI commands:

# Create a network for container communication
docker network create wp-network

# Run MySQL container
docker run -d \
  --name wp-mysql \
  --network wp-network \
  -e MYSQL_ROOT_PASSWORD=rootpassword \
  -e MYSQL_DATABASE=wordpress \
  -e MYSQL_USER=wordpressuser \
  -e MYSQL_PASSWORD=wordpress \
  -v mysql_data:/var/lib/mysql \
  mysql:5.7

# Run WordPress container
docker run -d \
  --name wp-app \
  --network wp-network \
  -p 8080:80 \
  -e WORDPRESS_DB_HOST=wp-mysql \
  -e WORDPRESS_DB_USER=wordpressuser \
  -e WORDPRESS_DB_PASSWORD=wordpress \
  -e WORDPRESS_DB_NAME=wordpress \
  -v wordpress_data:/var/www/html \
  wordpress:latest

After running these commands, WordPress will be available at http://localhost:8080.

Pain Points

While Docker CLI offers significant improvements over traditional VM deployments, it has its challenges:

  • Verbose commands: These long Docker commands are difficult to remember and prone to typos.
  • Manual container linking: Container relationships must be managed by hand.
  • Complex setups: As your application grows, CLI management becomes unwieldy.
  • Limited documentation: Commands aren't self-documenting for future reference.

Docker CLI gives you power tools, but no toolbelt to organize them. You have incredible capabilities but find yourself constantly Googling "how to link containers again?"

Phase 3 – Simplifying with Docker Compose

What is Docker Compose?

Docker Compose is a tool for defining and running multi-container Docker applications using a single YAML file. This declarative approach lets you configure services, networks, and volumes in one place, then create and start everything with a single command.

Anatomy of a docker-compose.yml for WordPress

Here's a complete docker-compose.yml file for WordPress:

version: '3.8'

services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    networks:
      - wp-network

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    ports:
      - "8080:80"
    restart: always
    volumes:
      - wordpress_data:/var/www/html
      - ./wp-content:/var/www/html/wp-content
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    networks:
      - wp-network

volumes:
  db_data:
  wordpress_data:

networks:
  wp-network:

This file defines two services (WordPress and MySQL), their configuration, networking, and volume mounts.

Deploying with Docker Compose

With our docker-compose.yml file created, deployment becomes trivially simple:

# Start the application
docker-compose up -d

# View running containers
docker-compose ps

# View logs
docker-compose logs

# Stop the application
docker-compose down

That's it! No more lengthy Docker CLI commands or manual networking. Just a single file and a few simple commands.

Benefits

Docker Compose offers several advantages:

  • Self-documenting: The compose file serves as documentation for your infrastructure.
  • Version controlled: Infrastructure changes can be tracked in Git.
  • Simplified operations: Common tasks reduced to single commands.
  • Environment consistency: Same setup works across development, staging, and production.

Now you've got automation with elegance. Docker Compose is like having a concert orchestra with a great conductor – all the containers play together harmoniously with minimal direction.

Comparing the Three Approaches

Feature VM Docker CLI Docker Compose
Setup Time 30-60 minutes 5-10 minutes 2-5 minutes
Commands to Remember Dozens 5-10 complex commands 3-5 simple commands
Reproducibility Low (manual steps) Medium (scripted commands) High (declarative config)
Documentation External guides Command history Self-documenting YAML
Resource Efficiency Low High High
Portability Low Medium High
Scaling Complexity High Medium Low
Backup Strategy Complex Moderate Simple (volume management)
Development Parity Low Medium High

Real-World Use Cases and When to Use What

VM-based Deployment

Best for:

  • Legacy systems with specific OS requirements
  • Organizations with existing VM-centric operations
  • Scenarios requiring complete isolation and dedicated resources

Example scenario: A corporate WordPress site with custom security requirements and integration with on-premise systems.

Docker CLI

Best for:

  • Development environments
  • Simple production setups
  • Quick prototyping
  • Learning container concepts

Example scenario: A developer setting up WordPress locally to test a custom theme or plugin.

Docker Compose

Best for:

  • Development teams collaborating on the same project
  • CI/CD pipelines
  • Consistent staging and production environments
  • Complex multi-container applications

Example scenario: A digital agency managing multiple WordPress sites with consistent environments across development, staging, and production.

Stepping into the Future

Our journey from VMs to containers mirrors the broader evolution of infrastructure management. Each approach brings increased automation, consistency, and developer productivity-core tenets of DevOps philosophy.

As you progress in your containerization journey, consider exploring:

  • Docker Swarm or Kubernetes for container orchestration at scale
  • CI/CD pipelines for automated testing and deployment
  • GitOps workflows for infrastructure as code management

Remember, the goal isn't containerization for its own sake, but finding the right balance of simplicity, reproducibility, and maintainability for your specific needs. Whether you're managing a personal blog or an enterprise web application, these modern deployment approaches offer compelling advantages over traditional methods.

The best part? You can incrementally adopt these practices, moving from VMs to Docker to Compose at your own pace, reaping benefits at each step along the way. That's the beauty of the DevOps journey-continuous improvement through incremental change.

Now go forth and containerize-your future self will thank you for the hours of troubleshooting you'll never have to do.