• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Dockerizing and Orchestrating Legacy WordPress Systems on Modern Google Cloud Infrastructure

Dockerizing and Orchestrating Legacy WordPress Systems on Modern Google Cloud Infrastructure

Containerizing the WordPress Application

The first critical step in modernizing a legacy WordPress deployment is to containerize the application. This involves creating Docker images for the WordPress core, plugins, themes, and any custom PHP code. We’ll focus on a multi-stage build to keep our final image lean and secure.

We’ll start with a base PHP image that includes necessary extensions. For this example, we’ll use PHP 8.2 with FPM, as it’s a common and performant choice. The Dockerfile will handle installing WordPress, copying application code, and configuring PHP-FPM.

Dockerfile for WordPress Application

# Stage 1: Builder
FROM php:8.2-fpm-alpine AS builder

# Install necessary PHP extensions and system dependencies
RUN apk add --no-cache \
    git \
    unzip \
    libzip-dev \
    libpng-dev \
    libjpeg-turbo-dev \
    freetype-dev \
    icu-dev \
    imagemagick-dev \
    libxml2-dev \
    zip \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install pdo pdo_mysql zip exif intl opcache

# Set working directory
WORKDIR /var/www/html

# Download and extract WordPress
RUN curl -o wordpress.tar.gz -SL https://wordpress.org/latest.tar.gz \
    && tar -xzf wordpress.tar.gz --strip-components=1 \
    && rm wordpress.tar.gz

# Copy custom themes, plugins, and uploads (if applicable)
# Ensure these directories exist in your build context
COPY ./wp-content/themes/ /var/www/html/wp-content/themes/
COPY ./wp-content/plugins/ /var/www/html/wp-content/plugins/
# COPY ./wp-content/uploads/ /var/www/html/wp-content/uploads/ # Be cautious with large uploads

# Copy custom PHP files or configurations
# COPY ./custom-php/ /var/www/html/

# Clean up build dependencies
RUN apk del git unzip libzip-dev libpng-dev libjpeg-turbo-dev freetype-dev icu-dev imagemagick-dev libxml2-dev \
    && rm -rf /var/cache/apk/*

# Stage 2: Production
FROM php:8.2-fpm-alpine AS production

# Install only runtime dependencies
RUN apk add --no-cache \
    libzip \
    libpng \
    libjpeg-turbo \
    freetype \
    icu \
    imagemagick \
    libxml2 \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install pdo pdo_mysql zip exif intl opcache

# Set working directory
WORKDIR /var/www/html

# Copy WordPress core and wp-content from the builder stage
COPY --from=builder /var/www/html/ /var/www/html/

# Ensure correct permissions for WordPress files
RUN chown -R www-data:www-data /var/www/html \
    && chmod -R 755 /var/www/html

# Expose the port PHP-FPM listens on
EXPOSE 9000

# Define the command to run PHP-FPM
CMD ["php-fpm"]

This Dockerfile uses a multi-stage build. The first stage (`builder`) installs build tools and extensions, downloads WordPress, and copies your custom content. The second stage (`production`) starts from a clean Alpine PHP image, installs only the necessary runtime libraries and PHP extensions, and then copies the built application from the `builder` stage. This results in a significantly smaller and more secure production image.

Nginx Configuration for WordPress

A robust web server like Nginx is essential for serving WordPress efficiently. We’ll create a separate Docker image for Nginx, configured to proxy requests to the PHP-FPM container and serve static assets directly.

Dockerfile for Nginx

FROM nginx:alpine

# Remove default Nginx configuration
RUN rm /etc/nginx/conf.d/default.conf

# Copy custom Nginx configuration
COPY nginx.conf /etc/nginx/conf.d/wordpress.conf

# Copy custom favicon or error pages if needed
# COPY html/ /usr/share/nginx/html/

# Expose port 80
EXPOSE 80

# Start Nginx in the foreground
CMD ["nginx", "-g", "daemon off;"]

Nginx Configuration File (nginx.conf)

server {
    listen 80;
    server_name localhost; # Replace with your domain name

    root /var/www/html;
    index index.php index.html index.htm;

    # Serve static files directly
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp|woff|woff2|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public";
        try_files $uri =404;
    }

    # Handle WordPress permalinks
    location / {
        try_files /index.php$is_args$args;
    }

    # Pass PHP scripts to FastCGI server
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress_app:9000; # Service name of the PHP-FPM container
        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;
    }

    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
}

In the nginx.conf, note the fastcgi_pass wordpress_app:9000; directive. This assumes your PHP-FPM container will be named wordpress_app when using Docker Compose. This configuration efficiently serves static assets and forwards PHP requests to the FPM process.

Database Containerization (MySQL/MariaDB)

A persistent and managed database is crucial. We’ll use the official MySQL or MariaDB Docker images. For production, it’s highly recommended to use a managed database service like Google Cloud SQL, but for demonstration and smaller deployments, a Dockerized database is viable.

Docker Compose for Local Orchestration

Docker Compose is an excellent tool for defining and running multi-container Docker applications. It allows us to orchestrate our WordPress app, Nginx, and database services.

docker-compose.yml

version: '3.8'

services:
  wordpress_app:
    build:
      context: ./wordpress # Path to your WordPress Dockerfile directory
      dockerfile: Dockerfile
    container_name: wordpress_app
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress_user
      WORDPRESS_DB_PASSWORD: wordpress_password
      WORDPRESS_DB_NAME: wordpress_db
      # Optional: For advanced configurations
      # WP_HOME: http://localhost
      # WP_SITEURL: http://localhost
    volumes:
      - wordpress_data:/var/www/html # Persist WordPress files
    networks:
      - app-network

  nginx:
    build:
      context: ./nginx # Path to your Nginx Dockerfile directory
      dockerfile: Dockerfile
    container_name: nginx
    restart: always
    ports:
      - "80:80" # Map host port 80 to container port 80
      - "443:443" # If using SSL
    volumes:
      - wordpress_data:/var/www/html # Read-only access to WordPress files
      - ./nginx/logs:/var/log/nginx # Persist Nginx logs
    depends_on:
      - wordpress_app
    networks:
      - app-network

  db:
    image: mysql:8.0 # Or mariadb:latest
    container_name: wordpress_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_password_here # CHANGE THIS
      MYSQL_DATABASE: wordpress_db
      MYSQL_USER: wordpress_user
      MYSQL_PASSWORD: wordpress_password # CHANGE THIS
    volumes:
      - db_data:/var/lib/mysql # Persist database data
    networks:
      - app-network

volumes:
  wordpress_data:
  db_data:

networks:
  app-network:
    driver: bridge

This docker-compose.yml defines three services: wordpress_app (PHP-FPM), nginx, and db (MySQL). It sets up shared volumes for data persistence and a custom network for inter-container communication. Remember to replace placeholder passwords and adjust paths to your Dockerfile contexts.

Deploying to Google Kubernetes Engine (GKE)

For production environments, orchestrating these containers on a managed Kubernetes service like GKE is the standard. We’ll translate our Docker Compose setup into Kubernetes manifests.

Kubernetes Deployment for WordPress App

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-app-deployment
  labels:
    app: wordpress-app
spec:
  replicas: 2 # Adjust as needed
  selector:
    matchLabels:
      app: wordpress-app
  template:
    metadata:
      labels:
        app: wordpress-app
    spec:
      containers:
      - name: wordpress-app
        image: your-gcr-repo/wordpress-app:latest # Replace with your GCR image
        ports:
        - containerPort: 9000
        env:
        - name: WORDPRESS_DB_HOST
          value: "mysql-service" # Kubernetes Service name for MySQL
        - name: WORDPRESS_DB_USER
          value: "wordpress_user"
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: wordpress-db-secrets
              key: db-password
        - name: WORDPRESS_DB_NAME
          value: "wordpress_db"
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wordpress-pvc

Kubernetes Deployment for Nginx

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2 # Adjust as needed
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: your-gcr-repo/nginx:latest # Replace with your GCR image
        ports:
        - containerPort: 80
        - containerPort: 443 # If using SSL
        volumeMounts:
        - name: nginx-logs
          mountPath: /var/log/nginx
        - name: wordpress-persistent-storage # Mount WordPress files read-only
          mountPath: /var/www/html
          readOnly: true
      volumes:
      - name: nginx-logs
        emptyDir: {} # Or use a PersistentVolumeClaim for logs
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wordpress-pvc

Kubernetes Deployment for MySQL/MariaDB

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deployment
  labels:
    app: mysql
spec:
  replicas: 1 # Typically one replica for a database
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0 # Or mariadb:latest
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: wordpress-db-secrets
              key: root-password
        - name: MYSQL_DATABASE
          value: "wordpress_db"
        - name: MYSQL_USER
          value: "wordpress_user"
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: wordpress-db-secrets
              key: db-password
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc

Kubernetes Services and Persistent Volumes

apiVersion: v1
kind: Service
metadata:
  name: wordpress-app-service
spec:
  selector:
    app: wordpress-app
  ports:
    - protocol: TCP
      port: 9000
      targetPort: 9000
  type: ClusterIP # Internal service

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
    - protocol: TCP
      port: 443
      targetPort: 443
  type: LoadBalancer # Expose Nginx to the internet

---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: mysql
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306
  type: ClusterIP # Internal service

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi # Adjust size as needed

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi # Adjust size as needed

Kubernetes Secrets for Database Credentials

apiVersion: v1
kind: Secret
metadata:
  name: wordpress-db-secrets
type: Opaque
data:
  db-password: YOUR_DB_PASSWORD_BASE64_ENCODED # e.g., echo -n 'your_password' | base64
  root-password: YOUR_ROOT_PASSWORD_BASE64_ENCODED # e.g., echo -n 'your_root_password' | base64

Before applying these manifests, ensure you have built your Docker images and pushed them to a container registry accessible by GKE (e.g., Google Container Registry – GCR). You’ll also need to create the Kubernetes secrets with your database credentials, base64 encoded. The nginx-service is of type LoadBalancer, which will provision a Google Cloud Load Balancer to expose your WordPress site. For production, consider using Google Cloud SQL for a managed database solution, which simplifies operations and improves reliability.

Advanced Considerations: Caching, CDN, and Security

To truly leverage modern infrastructure, consider these enhancements:

  • Object Caching: Integrate Redis or Memcached for WordPress object caching. This requires adding a Redis/Memcached service to your Kubernetes manifests and configuring WordPress (e.g., via a plugin like W3 Total Cache or Redis Object Cache) to use it.
  • CDN Integration: Use Google Cloud CDN or a third-party CDN to serve static assets. This offloads traffic from your Nginx pods and improves global performance.
  • SSL/TLS Termination: For production, terminate SSL at the Google Cloud Load Balancer (managed by the LoadBalancer service type) or use an Ingress controller with cert-manager for automated certificate management.
  • Database Management: As mentioned, Google Cloud SQL is the recommended path for production databases. It offers automated backups, replication, and high availability.
  • Monitoring and Logging: Integrate Google Cloud’s operations suite (formerly Stackdriver) for comprehensive monitoring, logging, and alerting.

By containerizing your legacy WordPress system and orchestrating it on GKE, you gain scalability, resilience, and the ability to manage your application using modern DevOps practices. This transition is a significant step towards a more robust and maintainable infrastructure.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability
  • Scala Pekko vs. Go Goroutines: Actor Model vs. CSP for Event-Driven Reactive Systems
  • Java Loom Virtual Threads vs. Go Goroutines: Under-the-Hood Scheduler and Thread Overhead Comparison

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (584)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (806)
  • PHP (5)
  • PHP Development (21)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (19)
  • Ruby on Rails (1)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (357)

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (806)
  • Debugging & Troubleshooting (584)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala