• 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 PHP Systems on Modern OVH Infrastructure

Dockerizing and Orchestrating Legacy PHP Systems on Modern OVH Infrastructure

Assessing Legacy PHP Application Dependencies

Before embarking on containerization, a thorough audit of the legacy PHP application’s dependencies is paramount. This involves identifying not only PHP extensions but also external services, system libraries, and specific configurations that the application relies upon. For a typical monolithic PHP application, this might include:

  • PHP version and required extensions (e.g., mysqli, gd, curl, openssl, intl).
  • Web server configuration (e.g., Apache .htaccess directives, Nginx rewrite rules).
  • Database connectivity details and specific SQL features used.
  • Caching mechanisms (e.g., Memcached, Redis).
  • Background job queues (e.g., Cron jobs, RabbitMQ).
  • File system permissions and expected directory structures.
  • Environment variables used for configuration.
  • Third-party libraries and their installation methods (e.g., Composer, manual inclusion).

Tools like phpinfo(), static code analysis, and manual inspection of deployment scripts are invaluable at this stage. Documenting these dependencies meticulously will form the basis of your Dockerfile and orchestration strategy.

Crafting the Dockerfile for a Monolithic PHP App

The Dockerfile is the blueprint for your container image. For a legacy PHP application, we’ll aim for a multi-stage build to keep the final image lean. We’ll start with a base PHP image, install necessary system packages and PHP extensions, and then copy the application code.

Consider a scenario where the application requires PHP 7.4, the mysqli and gd extensions, and uses Composer for dependency management. We’ll also need to install nginx as the web server.

Stage 1: Builder – Installing PHP Extensions and Composer Dependencies

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

# Install system dependencies for PHP extensions
RUN apk update && apk add --no-cache \
    libzip-dev \
    libpng-dev \
    libjpeg-turbo-dev \
    freetype-dev \
    icu-dev \
    git \
    zip \
    unzip \
    nginx

# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype-dir=/usr --with-jpeg-dir=/usr \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install mysqli \
    && docker-php-ext-install intl \
    && docker-php-ext-enable zip

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer

# Set working directory
WORKDIR /app

# Copy composer files and install dependencies
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction

Stage 2: Production – Assembling the Final Image

# Stage 2: Production
FROM php:7.4-fpm-alpine

# Install system dependencies for Nginx and PHP extensions (if not already in base image)
RUN apk update && apk add --no-cache \
    nginx \
    libzip \
    libpng \
    libjpeg-turbo \
    freetype \
    icu \
    zip \
    unzip

# Copy installed PHP extensions from builder stage
COPY --from=builder /usr/local/lib/php/extensions/no-debug-non-zts-20190902/ /usr/local/lib/php/extensions/no-debug-non-zts-20190902/

# Copy application code and Composer dependencies from builder stage
COPY --from=builder /app /var/www/html

# Copy Nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Expose port and define entrypoint
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Nginx Configuration for PHP-FPM

The nginx.conf file will route requests to the PHP-FPM process. It’s crucial to configure Nginx to serve static assets directly and pass PHP requests to the FPM socket.

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public; # Adjust if your public directory is different

    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass php-fpm:9000; # Assuming php-fpm service is named 'php-fpm' and listens on port 9000
        fastcgi_index index.php;
        fastcgi_read_timeout 300; # Increase timeout for long-running scripts
    }

    # Deny access to hidden files
    location ~ /\.ht {
        deny all;
    }

    # Serve static files directly
    location ~* \.(css|js|jpg|jpeg|gif|png|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, no-transform";
    }
}

Note the fastcgi_pass php-fpm:9000; directive. This assumes you’ll be running PHP-FPM as a separate service, which is a common pattern in Docker Compose. If you’re running Nginx and PHP-FPM in the same container (less recommended for scalability), you’d use unix:/var/run/php/php7.4-fpm.sock.

Orchestrating with Docker Compose on OVHcloud

Docker Compose is ideal for defining and running multi-container Docker applications. On OVHcloud, you can deploy these containers on various services like Public Cloud Instances, Managed Kubernetes (K8s), or even dedicated servers. For simplicity, we’ll focus on a Docker Compose setup that could run on a Public Cloud Instance.

version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html # Mount application code for development/testing
      - ./logs/nginx:/var/log/nginx # Mount Nginx logs
    depends_on:
      - php-fpm
    environment:
      - DB_HOST=database
      - DB_USER=user
      - DB_PASSWORD=password
      - DB_NAME=dbname
      # Add other environment variables as needed

  php-fpm:
    image: php:7.4-fpm-alpine # Use the same base image as the builder stage
    volumes:
      - ./app:/var/www/html # Mount application code
      - ./logs/php-fpm:/var/log/php-fpm # Mount PHP-FPM logs
    environment:
      - PHP_ERROR_LOG=/var/log/php-fpm/error.log
    # If running PHP-FPM in a separate container, you might need to install extensions here too,
    # or better, build a custom PHP-FPM image that includes them.
    # For this example, we assume the 'web' service's Dockerfile builds a complete image.
    # A more robust approach would be to have separate Dockerfiles for web and php-fpm.

  database:
    image: mysql:8.0
    ports:
      - "3306:3306" # Only expose if needed for external access, otherwise rely on internal Docker network
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: dbname
      MYSQL_USER: user
      MYSQL_PASSWORD: password

volumes:
  db_data:

In this docker-compose.yml:

  • The web service builds the application image using the Dockerfile. It maps port 80 and depends on php-fpm. We’ve included volume mounts for the application code and Nginx logs for easier debugging and development.
  • The php-fpm service uses a standard PHP-FPM image. In a production scenario, you’d likely build a custom PHP-FPM image with all necessary extensions to avoid runtime installations.
  • The database service uses a MySQL image and sets up persistent storage using a named volume db_data.
  • All services are on the same default Docker network, allowing them to communicate using their service names (e.g., database, php-fpm).

Deployment on OVHcloud Public Cloud Instances

Once you have your Dockerfile and docker-compose.yml, you can deploy this on an OVHcloud Public Cloud Instance. The process involves:

  1. Provision an Instance: Choose a suitable Public Cloud Instance (e.g., a General Purpose instance) and install Docker and Docker Compose on it.
  2. Transfer Files: Securely copy your application code, Dockerfile, and docker-compose.yml to the instance using scp or rsync.
  3. Build and Run: Navigate to the directory containing your files on the instance and run:
# SSH into your OVHcloud instance
ssh user@your_instance_ip

# Navigate to your project directory
cd /path/to/your/legacy-app

# Build the Docker images (use --build-arg for dynamic values if needed)
docker-compose build

# Start the containers in detached mode
docker-compose up -d

To manage the lifecycle of your application, you can use commands like docker-compose ps to view running containers, docker-compose logs -f web to tail Nginx logs, and docker-compose down to stop and remove containers.

Leveraging OVHcloud Managed Kubernetes (K8s)

For more robust orchestration, scalability, and high availability, deploying on OVHcloud Managed Kubernetes is the recommended path. This involves converting your Docker Compose setup into Kubernetes manifests (Deployments, Services, Ingress, etc.).

Key Steps:

  • Container Registry: Push your built Docker images to a container registry accessible by your Kubernetes cluster (e.g., OVHcloud’s Container Registry, Docker Hub, or a private registry).
  • Kubernetes Manifests: Create YAML files for Deployments (for your web and PHP-FPM services), a StatefulSet or Deployment for your database, Services to expose them internally, and an Ingress resource to manage external access.
  • Ingress Controller: Ensure you have an Ingress controller (like Nginx Ingress Controller) deployed in your K8s cluster. OVHcloud Managed Kubernetes often provides this or makes it easy to install.
  • Persistent Volumes: Configure Persistent Volumes (PVs) and Persistent Volume Claims (PVCs) for your database to ensure data persistence. OVHcloud offers various storage classes for this.

Here’s a simplified example of a Kubernetes Deployment for your web service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: legacy-app-web
  labels:
    app: legacy-app
    tier: web
spec:
  replicas: 3 # Scale as needed
  selector:
    matchLabels:
      app: legacy-app
      tier: web
  template:
    metadata:
      labels:
        app: legacy-app
        tier: web
    spec:
      containers:
      - name: web
        image: your-registry/legacy-app-web:latest # Replace with your image
        ports:
        - containerPort: 80
        env:
        - name: DB_HOST
          value: "legacy-app-database" # Service name for the database
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        - name: DB_NAME
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: database
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
      # Add readiness and liveness probes for better health management
      # readinessProbe:
      #   httpGet:
      #     path: /healthz # Assuming you have a health check endpoint
      #     port: 80
      #   initialDelaySeconds: 5
      #   periodSeconds: 5

You would then create corresponding Service resources for web and php-fpm, and an Ingress resource to route external traffic. For the database, a StatefulSet with a PersistentVolumeClaim is generally preferred for stateful applications.

Monitoring and Logging Strategies

Effective monitoring and logging are critical for maintaining the health and performance of your containerized legacy application. On OVHcloud, you can leverage:

  • Docker Logs: Use docker logs or docker-compose logs for immediate access to container output.
  • Centralized Logging: Integrate with a centralized logging solution like ELK Stack (Elasticsearch, Logstash, Kibana) or Grafana Loki. You can configure log drivers in Docker or use agents like Filebeat to ship logs from containers to your central store.
  • Metrics: Collect metrics from your containers using tools like Prometheus. You can expose metrics from your PHP application (e.g., using a Prometheus client library) and scrape them.
  • Application Performance Monitoring (APM): For deep insights into PHP execution, consider APM tools like New Relic, Datadog, or open-source alternatives like Jaeger.
  • OVHcloud Monitoring Tools: Utilize OVHcloud’s built-in monitoring dashboards for instance-level metrics (CPU, memory, network) and explore their managed logging and monitoring services if available.

For instance, to configure Nginx to log to stdout (which Docker can capture), you might modify nginx.conf:

# In nginx.conf
error_log /var/log/nginx/error.log warn; # Default to file
access_log /var/log/nginx/access.log combined; # Default to file

# To log to stdout, you'd typically configure the container's entrypoint script
# to redirect these logs or use a log driver. For simplicity, ensure your Dockerfile
# or docker-compose.yml mounts a volume for logs and then use `docker logs` or
# `docker-compose logs` to view them. For advanced setups, consider Fluentd or Logstash.

When using Docker Compose, ensure your docker-compose.yml includes volume mounts for logs, as shown in the earlier example, to facilitate easy access and redirection to a centralized logging system.

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