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.