Dockerizing and Orchestrating Legacy WooCommerce Systems on Modern Linode Infrastructure
Assessing the Legacy WooCommerce Monolith
Before embarking on containerization, a thorough assessment of the existing WooCommerce installation is paramount. This involves identifying dependencies, database schemas, file system structures, and any custom plugins or themes that might pose challenges in a containerized environment. Legacy systems often have implicit dependencies on specific PHP versions, extensions, or even operating system-level libraries. Documenting these is the first step to a successful migration.
Key areas to scrutinize:
- PHP version and required extensions (e.g.,
gd,imagick,mysqli,curl,mbstring). - Web server configuration (Apache or Nginx) and any custom rewrite rules.
- Database version and specific configurations (e.g., character set, collation).
- File permissions and ownership for the WordPress/WooCommerce directory.
- Any external services or APIs the store relies on.
- Custom code, plugins, and themes – these are often the most complex to containerize.
Crafting the Dockerfile for WooCommerce
We’ll start with a robust base image and layer our WooCommerce installation on top. For this example, we’ll use an official PHP image with Apache, as many legacy setups are Apache-centric. We’ll also include common PHP extensions required by WordPress and WooCommerce.
Create a file named Dockerfile in your project’s root directory:
# Use an official PHP image with Apache
FROM php:8.1-apache
# Install necessary PHP extensions and system packages
RUN apt-get update && apt-get install -y \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev \
unzip \
git \
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install zip \
&& docker-php-ext-install pdo_mysql \
&& docker-php-ext-install mysqli \
&& docker-php-ext-install curl \
&& docker-php-ext-install mbstring \
&& docker-php-ext-install exif \
&& a2enmod rewrite \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /var/www/html
# Copy WordPress and WooCommerce files
# Assuming you have a 'wordpress' directory in the same location as your Dockerfile
COPY wordpress/ .
# Copy custom Apache configuration if needed
# COPY apache/000-default.conf /etc/apache2/sites-available/000-default.conf
# Ensure correct permissions for Apache to write to the web root
RUN chown -R www-data:www-data /var/www/html && chmod -R 755 /var/www/html
# Download and install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Install WordPress/WooCommerce dependencies if using Composer for plugins/themes
# If your legacy setup doesn't use Composer, this step might be skipped or adapted.
# COPY composer.json composer.lock ./
# RUN composer install --no-dev --optimize-autoloader
# Expose port 80
EXPOSE 80
# Start Apache in the foreground
CMD ["apache2-foreground"]
Explanation:
- We start with a PHP 8.1 Apache image. Adjust the PHP version if your legacy system requires an older one (e.g.,
php:7.4-apache). - Essential system packages and PHP extensions are installed. The
gdextension is configured with freetype, jpeg, and webp support, crucial for image manipulation in WooCommerce. a2enmod rewriteenables Apache’s rewrite module, necessary for WordPress permalinks.- The working directory is set to Apache’s default web root.
- We copy the WordPress and WooCommerce files. It’s highly recommended to have your entire WordPress installation (including plugins and themes) in a local directory named
wordpressalongside yourDockerfile. - Permissions are set for the Apache user (
www-data) to write to the web root. - Composer is installed globally, useful for managing PHP dependencies. If your legacy setup relies heavily on Composer for plugins or custom code, uncomment and adapt the Composer installation steps.
- Port 80 is exposed.
apache2-foregroundensures Apache runs in the foreground, which is standard for Docker containers.
Database Containerization and Configuration
A separate container for the database is essential for a robust, scalable setup. We’ll use the official MySQL image. For legacy systems, ensuring compatibility with the existing database version is critical. If your legacy system uses an older MySQL version, consider using a specific tag (e.g., mysql:5.7).
For local development and testing, a docker-compose.yml file is invaluable. This file defines and links our services (WooCommerce app and database).
version: '3.8'
services:
db:
image: mysql:8.0 # Or your legacy MySQL version, e.g., mysql:5.7
container_name: woocommerce_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: your_root_password # Change this!
MYSQL_DATABASE: woocommerce_db
MYSQL_USER: woocommerce_user
MYSQL_PASSWORD: woocommerce_password # Change this!
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
- ./mysql/initdb.d:/docker-entrypoint-initdb.d # For custom initialization scripts
wordpress:
build:
context: .
dockerfile: Dockerfile
container_name: woocommerce_app
restart: always
ports:
- "8080:80" # Map host port 8080 to container port 80
depends_on:
- db
environment:
WORDPRESS_DB_HOST: db:3306 # Service name 'db' is the hostname
WORDPRESS_DB_NAME: woocommerce_db
WORDPRESS_DB_USER: woocommerce_user
WORDPRESS_DB_PASSWORD: woocommerce_password # Must match db service password
volumes:
- ./wordpress:/var/www/html # Mount your local wordpress directory for development
# For production, consider named volumes for better performance and persistence
# - woocommerce_files:/var/www/html
volumes:
db_data:
# woocommerce_files: # Uncomment if using named volume for app files
Database Initialization (Optional but Recommended):
To ensure correct character sets and collations, especially if your legacy database uses specific settings, create a directory named mysql/initdb.d in your project root and place SQL files there. For example, mysql/initdb.d/01-init.sql:
-- Set character set and collation for the database CREATE DATABASE IF NOT EXISTS woocommerce_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Grant privileges to the user GRANT ALL PRIVILEGES ON woocommerce_db.* TO 'woocommerce_user'@'%'; FLUSH PRIVILEGES;
Explanation:
- We define two services:
db(MySQL) andwordpress(our WooCommerce app). - The
dbservice uses a MySQL image, sets root and database credentials, and maps port 3306. A named volumedb_datais used for persistent database storage. Thedocker-entrypoint-initdb.ddirectory allows custom SQL scripts to run on first container startup. - The
wordpressservice builds from ourDockerfile. It depends on thedbservice, ensuring the database is available before the app starts. - Environment variables are used to pass database connection details to WordPress. Crucially,
WORDPRESS_DB_HOSTis set to the service namedb, which Docker’s internal DNS resolves to the database container’s IP. - For development, we mount the local
./wordpressdirectory into the container. For production, consider using named volumes (e.g.,woocommerce_files) for better performance and management. - The application port 80 is mapped to host port 8080 to avoid conflicts with any existing web server on the host.
Building and Running the Containers
Navigate to your project’s root directory (where Dockerfile and docker-compose.yml reside) in your terminal.
Build the Docker images:
docker-compose build
Start the containers in detached mode:
docker-compose up -d
You can check the logs for each service:
docker-compose logs -f wordpress docker-compose logs -f db
Once the containers are running, you should be able to access your WooCommerce store at http://localhost:8080 (or the IP address of your Linode instance if deploying there directly). The initial WordPress setup wizard will guide you through the final steps.
Migrating Data to Linode Infrastructure
For production deployment on Linode, you’ll typically use a managed Kubernetes service (like Linode Kubernetes Engine) or deploy directly onto Linode Compute Instances with Docker Compose. The principles remain similar, but orchestration becomes more critical.
Database Migration:
- Backup existing database: Use
mysqldumpto create a full backup of your current WooCommerce database. - Transfer backup: Securely transfer the backup file to your Linode instance.
- Import into containerized DB: If using Docker Compose on a Linode instance, you can exec into the running database container and import the dump, or use the
initdb.dmechanism with a pre-compressed dump. For managed databases (like Linode’s Managed Databases), follow their import procedures.
# On your Linode instance with Docker Compose running # Assuming your dump file is named 'wp_backup.sql.gz' docker exec -i woocommerce_db sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"' < wp_backup.sql.gz
File Migration:
The wp-content/uploads directory is the most critical for file migration. Use rsync to efficiently transfer these files to the persistent volume designated for your WordPress application on Linode.
# On your Linode instance, assuming you've configured persistent volumes # and your local uploads are in 'local_wp_content/uploads' rsync -avzP local_wp_content/uploads/ /var/lib/docker/volumes/woocommerce_files/_data/wp-content/uploads/
Orchestration with Docker Compose on Linode
For a single Linode Compute Instance, deploying with docker-compose is straightforward. Ensure your docker-compose.yml is configured for production:
- Use named volumes for database and application files for better persistence and management.
- Consider using a reverse proxy (like Nginx or Traefik) as a separate container to handle SSL termination, load balancing (if you scale horizontally), and routing requests to your WooCommerce container.
- Secure your database credentials and root passwords. Use environment variables injected securely, not hardcoded.
Example docker-compose.yml for Production (simplified):
version: '3.8'
services:
db:
image: mysql:8.0
container_name: woocommerce_db_prod
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} # Use .env file
MYSQL_DATABASE: woocommerce_db
MYSQL_USER: woocommerce_user
MYSQL_PASSWORD: ${MYSQL_PASSWORD} # Use .env file
volumes:
- db_data_prod:/var/lib/mysql
wordpress:
build:
context: .
dockerfile: Dockerfile
container_name: woocommerce_app_prod
restart: always
ports:
- "80:80" # Map to host port 80 for direct access or reverse proxy
depends_on:
- db
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: woocommerce_db
WORDPRESS_DB_USER: woocommerce_user
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD} # Match db service password
volumes:
- woocommerce_files_prod:/var/www/html # Use named volume
volumes:
db_data_prod:
woocommerce_files_prod:
Create a .env file in the same directory as your docker-compose.yml to store sensitive credentials:
MYSQL_ROOT_PASSWORD=your_super_secret_root_password MYSQL_PASSWORD=your_super_secret_db_password
Then, deploy using:
docker-compose -f docker-compose.yml up -d
Advanced Considerations: Linode Kubernetes Engine (LKE)
For true scalability and resilience, orchestrating with Kubernetes on Linode Kubernetes Engine (LKE) is the next step. This involves defining Kubernetes manifests (Deployments, Services, PersistentVolumeClaims, Secrets).
Key Kubernetes Objects:
- Deployment: Manages the WooCommerce application pods, ensuring a desired number of replicas are running.
- StatefulSet: Often preferred for databases to guarantee stable network identifiers, persistent storage, and ordered deployment/scaling.
- Service: Provides stable IP addresses and DNS names for accessing your database and application pods. An Ingress controller will manage external access to the WooCommerce service.
- PersistentVolumeClaim (PVC): Requests storage from Linode’s block storage for your database and WooCommerce files.
- Secret: Stores sensitive information like database credentials.
Your Dockerfile remains the same. You would then create Kubernetes YAML files. For instance, a simplified WooCommerce Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: woocommerce-app
labels:
app: woocommerce
spec:
replicas: 3 # Scale as needed
selector:
matchLabels:
app: woocommerce
template:
metadata:
labels:
app: woocommerce
spec:
containers:
- name: woocommerce
image: your-docker-registry/woocommerce:latest # Push your image here
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: "mysql-service:3306" # Service name for your DB
- name: WORDPRESS_DB_NAME
valueFrom:
secretKeyRef:
name: woocommerce-secrets
key: db-name
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: woocommerce-secrets
key: db-user
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: woocommerce-secrets
key: db-password
volumeMounts:
- name: woocommerce-persistent-storage
mountPath: /var/www/html # Or your specific WordPress root
volumes:
- name: woocommerce-persistent-storage
persistentVolumeClaim:
claimName: woocommerce-pvc
You would also define a corresponding StatefulSet for your MySQL database, a Service for internal communication, a PersistentVolumeClaim for storage, and a Secret for credentials. An Ingress resource would then expose your WooCommerce application to the internet, often with SSL termination handled by a cloud controller (like Nginx Ingress Controller).
This approach provides high availability, automatic scaling, and robust management for your legacy WooCommerce system on modern cloud infrastructure.