Dockerizing and Orchestrating Legacy WordPress Systems on Modern OVH Infrastructure
Assessing Legacy WordPress for Containerization
Before embarking on the Dockerization journey for a legacy WordPress installation, a thorough assessment is paramount. This involves identifying dependencies, understanding the existing infrastructure, and pinpointing potential compatibility issues with containerized environments. Legacy systems often have hardcoded paths, specific PHP version requirements, and custom configurations that need careful extraction and adaptation.
Key areas to scrutinize include:
- PHP Version: Determine the exact PHP version and any required extensions (e.g.,
gd,imagick,mysqli,mbstring). Legacy sites might be tied to older, unsupported PHP versions, necessitating careful consideration of security implications and potential upgrade paths. - Database: Identify the database type (typically MySQL/MariaDB) and version. Note any specific configurations or stored procedures.
- File System: Map out the WordPress core, themes, plugins, and uploads directories. Understand any custom directory structures or symbolic links.
- External Dependencies: Document any external services or APIs the WordPress site interacts with (e.g., caching layers, email services, CDNs, custom API endpoints).
- Custom Code: Analyze any custom themes, plugins, or modifications to core WordPress files. These are often the most challenging aspects to containerize due to potential hardcoded paths or system-level dependencies.
- Cron Jobs: Identify any server-level cron jobs that interact with the WordPress site, such as WP-CLI commands or custom scripts.
Crafting the Dockerfile for WordPress Core and Plugins
We’ll start by building a robust Dockerfile that encapsulates the WordPress application. This involves selecting an appropriate base image, installing necessary software, and configuring the WordPress environment.
Consider a multi-stage build to keep the final image lean. The first stage can handle dependency installation and compilation, while the second stage copies only the necessary artifacts.
Here’s a sample Dockerfile, assuming a PHP 8.1 environment with common extensions. Adjust the PHP version and extensions based on your legacy system’s requirements.
Dockerfile Example
# Stage 1: Builder
FROM php:8.1-fpm-buster AS builder
# Install system dependencies for PHP extensions
RUN apt-get update && apt-get install -y \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev \
libssl-dev \
libonig-dev \
unzip \
git \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd zip pdo_mysql mbstring exif opcache \
&& pecl install redis \
&& docker-php-ext-enable redis
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
# Set working directory
WORKDIR /var/www/html
# Copy WordPress core and plugins (assuming they are in a local 'wordpress' directory)
# For legacy systems, you might need to copy your existing WordPress installation here.
COPY wordpress/ .
# Install Composer dependencies for plugins if any
# RUN composer install --no-dev --optimize-autoloader
# Clean up apt cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Stage 2: Production Image
FROM php:8.1-fpm-buster
# Install only runtime dependencies
RUN apt-get update && apt-get install -y \
libzip4 \
libpng16-16 \
libjpeg62-turbo \
libfreetype6 \
libwebp6 \
libssl1.1 \
libonig5 \
&& rm -rf /var/lib/apt/lists/*
# Copy PHP extensions from builder stage
COPY --from=builder /usr/local/lib/php/extensions/no-debug-non-zts-20210902/ /usr/local/lib/php/extensions/no-debug-non-zts-20210902/
# Enable PHP extensions
RUN docker-php-ext-enable gd zip pdo_mysql mbstring exif redis opcache
# Set working directory
WORKDIR /var/www/html
# Copy WordPress core and plugins from builder stage
COPY --from=builder /var/www/html/ .
# Ensure correct ownership for WordPress files
RUN chown -R www-data:www-data /var/www/html
# Expose port 9000 for PHP-FPM
EXPOSE 9000
# Default command to start PHP-FPM
CMD ["php-fpm"]
Configuring Nginx as a Reverse Proxy
A separate Nginx container will serve as the web server and reverse proxy to the PHP-FPM container. This separation provides flexibility and allows for easier management of web server configurations.
Nginx Configuration Example
server {
listen 80;
server_name your-domain.com; # Replace with your domain
root /var/www/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # 'wordpress' is the service name in docker-compose
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to sensitive files
location ~ /\.ht {
deny all;
}
# Caching for static assets (optional, but recommended)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
}
Setting up the Database Container (MariaDB/MySQL)
A dedicated database container is crucial for a robust WordPress deployment. We’ll use MariaDB as an example, a popular drop-in replacement for MySQL.
Database Container Configuration
services:
db:
image: mariadb:10.6 # Use a specific version for stability
container_name: wordpress_db
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: your_root_password # Change this
MYSQL_DATABASE: wordpress_db
MYSQL_USER: wordpress_user
MYSQL_PASSWORD: your_db_password # Change this
ports:
- "3306:3306" # Expose only if external access is needed for debugging
volumes:
db_data:
Orchestrating with Docker Compose
Docker Compose is the ideal tool for defining and running multi-container Docker applications. It allows us to define our WordPress, Nginx, and database services in a single YAML file.
docker-compose.yml Example
version: '3.8'
services:
wordpress:
build:
context: .
dockerfile: Dockerfile
container_name: wordpress_app
volumes:
- wp_content:/var/www/html/wp-content # Persist uploads and themes/plugins
- ./custom-config/php.ini:/usr/local/etc/php/conf.d/custom.ini # For custom PHP settings
depends_on:
- db
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress_db
WORDPRESS_DB_USER: wordpress_user
WORDPRESS_DB_PASSWORD: your_db_password # Must match db service password
restart: always
networks:
- app-network
nginx:
image: nginx:stable-alpine
container_name: wordpress_nginx
ports:
- "80:80"
- "443:443" # If using SSL
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d # Mount Nginx configuration
- ./certs:/etc/nginx/certs # Mount SSL certificates if used
- wp_content:/var/www/html/wp-content # Share wp-content for static asset caching
depends_on:
- wordpress
restart: always
networks:
- app-network
db:
image: mariadb:10.6
container_name: wordpress_db
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: your_root_password
MYSQL_DATABASE: wordpress_db
MYSQL_USER: wordpress_user
MYSQL_PASSWORD: your_db_password
networks:
- app-network
volumes:
wp_content:
db_data:
networks:
app-network:
Migrating Legacy Data and Configuration
This is often the most critical and time-consuming phase. For legacy systems, direct copying of files might not suffice. We need to ensure data integrity and compatibility.
Database Migration
1. Export Existing Database: Use mysqldump to create a backup of your current WordPress database.
mysqldump -u [your_db_user] -p [your_db_name] > wordpress_backup.sql
2. Import into Containerized DB: Once the database container is running, you can import the data. The easiest way is to mount the SQL file into a temporary container or use kubectl exec if deploying on Kubernetes.
# Using docker exec (assuming db container is named 'wordpress_db') docker exec -i wordpress_db sh -c 'exec mysql -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"' < wordpress_backup.sql
File System Migration
1. Copy WordPress Files: Copy your existing WordPress installation (excluding the database dump) into the `wordpress` directory that your Dockerfile expects, or directly into the volume mount for the `wordpress` service if you’re not building from source.
# Assuming your legacy WordPress is in /var/www/html/legacy-wp cp -R /var/www/html/legacy-wp/* . # Copy into the directory where Dockerfile expects it
2. Update `wp-config.php`: Ensure your `wp-config.php` file within the copied WordPress files uses the correct database credentials defined in your `docker-compose.yml`.
// Inside your wp-config.php define( 'DB_HOST', 'db:3306' ); // Use the service name and port define( 'DB_NAME', 'wordpress_db' ); define( 'DB_USER', 'wordpress_user' ); define( 'DB_PASSWORD', 'your_db_password' );
3. Handle Uploads: The `wp_content` volume mount will ensure that your existing `wp-content` directory (themes, plugins, uploads) is persisted. If you’re migrating from a very old setup, ensure file permissions are correctly set after copying.
Deploying on OVH Infrastructure
OVH offers several solutions suitable for containerized WordPress deployments, ranging from managed Kubernetes (OVHcloud Managed Kubernetes) to Virtual Private Servers (VPS) where you can run Docker directly.
Option 1: OVHcloud Managed Kubernetes (EKS)
For production environments requiring scalability, high availability, and robust orchestration, Managed Kubernetes is the recommended path. The process involves:
- Setting up a Managed Kubernetes Cluster: Provision a cluster through the OVHcloud control panel.
- Creating Kubernetes Manifests: Convert your
docker-compose.ymlinto Kubernetes YAML files (Deployments, Services, PersistentVolumeClaims, Ingress). - Deploying to the Cluster: Use
kubectl apply -f your-manifests.yamlto deploy your application. - Configuring Ingress: Use OVH’s Load Balancer or an Ingress Controller (like Nginx Ingress) to expose your WordPress application to the internet.
Example Kubernetes Deployment Snippet (WordPress Service):
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 2 # Scale as needed
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: your-dockerhub-repo/wordpress:latest # Your custom image
ports:
- containerPort: 9000
env:
- name: WORDPRESS_DB_HOST
value: wordpress-db-service:3306
- name: WORDPRESS_DB_NAME
value: wordpress_db
- name: WORDPRESS_DB_USER
value: wordpress_user
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: wordpress-db-secrets
key: db-password
volumeMounts:
- name: wp-content-volume
mountPath: /var/www/html/wp-content
volumes:
- name: wp-content-volume
persistentVolumeClaim:
claimName: wordpress-content-pvc
---
apiVersion: v1
kind: Service
metadata:
name: wordpress-service
spec:
selector:
app: wordpress
ports:
- protocol: TCP
port: 9000
targetPort: 9000
type: ClusterIP
Option 2: Docker on OVH VPS
For simpler deployments or development environments, running Docker directly on an OVH VPS is a viable option. The steps are:
- Provision an OVH VPS: Choose a VPS instance with sufficient resources.
- Install Docker and Docker Compose: Follow the official Docker installation guides for your VPS’s operating system (e.g., Ubuntu).
- Copy Project Files: Transfer your
Dockerfile,docker-compose.yml, and Nginx configuration to the VPS. - Build and Run: Navigate to your project directory on the VPS and execute
docker-compose up -d. - Configure Firewall: Ensure ports 80 and 443 are open in the VPS firewall.
Post-Deployment Considerations and Best Practices
Once your legacy WordPress system is containerized and deployed, several ongoing tasks and best practices are essential for maintaining a healthy and secure environment.
Logging and Monitoring
Centralized logging is crucial. Configure your containers to log to stdout and stderr, and use a log aggregation tool (like ELK stack, Grafana Loki, or cloud-native logging services) to collect and analyze logs from all containers. Monitor container health, resource utilization (CPU, memory), and application-specific metrics.
Backups
Implement a robust backup strategy for both the database and the persistent volumes (especially `wp-content` and `db_data`). For Docker on VPS, this might involve scheduled scripts that dump the database and archive volume data. On Kubernetes, leverage PersistentVolume snapshots or dedicated backup solutions.
Security Updates
Regularly update your base Docker images, PHP versions, and WordPress core, themes, and plugins. Automate security scanning of your container images to detect vulnerabilities. For legacy systems, this might involve testing updates in a staging environment before deploying to production.
Performance Tuning
Leverage Nginx caching, PHP OPcache, and consider external caching solutions like Redis or Memcached (which can also be run as separate containers). Optimize database queries and WordPress configurations for performance within the containerized environment.
Environment Variables
Utilize environment variables for sensitive information (database credentials, API keys) and configuration settings. This keeps secrets out of your Dockerfiles and configuration files, making them more portable and secure.