• 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 Shopify Systems on Modern Linode Infrastructure

Dockerizing and Orchestrating Legacy Shopify Systems on Modern Linode Infrastructure

Deconstructing the Legacy Shopify Monolith for Containerization

Many established e-commerce businesses find themselves with a “legacy” Shopify system. This often means a monolithic application, tightly coupled with custom themes, numerous third-party apps (some of which might be poorly architected or have limited API access), and potentially a complex, un-containerized backend infrastructure. The goal is to migrate this to a modern, scalable, and manageable environment on Linode using Docker and orchestration. This isn’t a simple lift-and-shift; it requires a strategic decomposition.

The first step is to identify the core components that can be containerized. For a typical Shopify setup, this includes:

  • The Shopify application itself (often a PHP-based monolith, though Shopify’s core is SaaS, we’re talking about the custom code, themes, and potentially backend services that interact with it).
  • Database (e.g., MySQL, PostgreSQL).
  • Caching layers (e.g., Redis, Memcached).
  • Background job processors (e.g., Sidekiq for Ruby, or custom PHP queues).
  • Static asset serving (often handled by Shopify CDN, but custom assets might need a dedicated solution).
  • Any custom microservices or APIs that the Shopify store relies on.

For this exercise, we’ll assume a common scenario: a PHP-based custom backend service that interacts with Shopify’s API, a MySQL database, and a Redis cache. We’ll aim to containerize these and orchestrate them using Docker Compose for initial deployment on Linode.

Crafting Dockerfiles for Core Components

We’ll start by creating Dockerfiles for each distinct service. This ensures isolation and reproducibility.

Dockerfile for the Custom PHP Backend Service

This Dockerfile will build an image for our custom PHP application. We’ll use an official PHP-FPM image as a base, install necessary extensions, copy our application code, and configure Nginx to serve it.

# Dockerfile for PHP Backend Service
FROM php:8.2-fpm

# Install system dependencies and PHP extensions
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libonig-dev \
    libxml2-dev \
    libssl-dev \
    libcurl4-openssl-dev \
    libicu-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 intl opcache \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

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

# Set working directory
WORKDIR /var/www/html

# Copy application code
COPY . /var/www/html

# Install Composer dependencies
RUN composer install --no-dev --optimize-autoloader

# Expose port
EXPOSE 9000

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

This Dockerfile assumes your PHP application is structured to run with PHP-FPM. You’ll need to adjust the `COPY . /var/www/html` line if your application code resides in a subdirectory.

Dockerfile for Nginx (to serve PHP and static assets)

A separate Nginx container is crucial for efficient static asset serving and proxying PHP requests to the PHP-FPM container.

# Dockerfile for Nginx
FROM nginx:stable-alpine

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

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

# Copy static assets (if any are managed outside Shopify CDN)
# COPY html /usr/share/nginx/html

# Expose port
EXPOSE 80

The `nginx.conf` file will be critical. Here’s a sample:

# nginx.conf
server {
    listen 80;
    server_name your-domain.com; # Replace with your actual domain

    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$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm-service:9000; # Service name from docker-compose
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

    # Cache static assets for a year
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public";
    }
}

Note the `fastcgi_pass php-fpm-service:9000;` line. `php-fpm-service` is the name we’ll assign to our PHP-FPM container in `docker-compose.yml`. This is how containers on the same Docker network communicate.

Docker Compose for Orchestration

Docker Compose is ideal for defining and running multi-container Docker applications. It allows us to configure our services, networks, and volumes in a single YAML file.

# docker-compose.yml
version: '3.8'

services:
  php-fpm-service:
    build:
      context: ./php-app # Directory containing your PHP Dockerfile
      dockerfile: Dockerfile
    container_name: php_fpm_app
    volumes:
      - ./php-app:/var/www/html # Mount your PHP app code for development/debugging
      # For production, you might want to copy code during build and not mount
    networks:
      - app-network
    depends_on:
      - mysql
      - redis

  nginx-service:
    image: nginx:stable-alpine
    container_name: nginx_webserver
    ports:
      - "80:80" # Map host port 80 to container port 80
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf # Mount custom Nginx config
      - ./php-app/public:/var/www/html/public # Mount public directory for Nginx
      # If you have static assets outside public, mount them here
    networks:
      - app-network
    depends_on:
      - php-fpm-service

  mysql-db:
    image: mysql:8.0
    container_name: mysql_database
    environment:
      MYSQL_ROOT_PASSWORD: your_strong_root_password # CHANGE THIS
      MYSQL_DATABASE: shopify_db
      MYSQL_USER: shopify_user
      MYSQL_PASSWORD: your_strong_db_password # CHANGE THIS
    volumes:
      - mysql_data:/var/lib/mysql # Persist database data
    networks:
      - app-network

  redis-cache:
    image: redis:latest
    container_name: redis_cache
    ports:
      - "6379:6379" # Optional: expose Redis to host for debugging
    volumes:
      - redis_data:/data # Persist Redis data
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  mysql_data:
  redis_data:

Key considerations in this `docker-compose.yml`:

  • `build` context for `php-fpm-service`: Points to the directory containing the PHP Dockerfile.
  • Volumes for PHP app: The `./php-app:/var/www/html` mount is useful for development. In a production CI/CD pipeline, you’d typically build the code into the image during the `docker build` step and omit this host mount for better performance and security.
  • `ports` for `nginx-service`: Exposes port 80 on the Linode host to the Nginx container.
  • `depends_on`: Ensures services start in a logical order (e.g., database before application). Note that `depends_on` only guarantees start order, not readiness. For robust applications, you’d implement health checks or retry mechanisms.
  • `networks`: All containers are on a custom bridge network (`app-network`), allowing them to communicate using their service names.
  • `volumes` for persistence: `mysql_data` and `redis_data` ensure that your database and cache data survive container restarts or removals.

Deployment on Linode

Once you have your Dockerfiles and `docker-compose.yml` ready, deploying to Linode is straightforward.

1. Provision a Linode Instance

Choose a Linode instance size appropriate for your expected load. A general-purpose instance with sufficient RAM and CPU should suffice for a moderately trafficked legacy Shopify store. Ensure you have SSH access configured.

2. Install Docker and Docker Compose

Connect to your Linode instance via SSH and install Docker and Docker Compose. The exact commands can vary slightly based on your chosen Linux distribution (e.g., Ubuntu, Debian).

# Install Docker (example for Ubuntu)
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io

# Add your user to the docker group to run docker commands without sudo
sudo usermod -aG docker $USER
newgrp docker # Apply group changes immediately

# Install Docker Compose (check for the latest version on GitHub releases)
LATEST_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
sudo curl -L "https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

3. Deploy Your Application

Transfer your application code, Dockerfiles, and `docker-compose.yml` to your Linode instance. A common method is using `git clone` if your code is in a repository, or `scp`/`rsync` for direct file transfer.

# Navigate to your project directory on Linode
cd /path/to/your/project

# Build and start the containers
docker-compose up -d

The `-d` flag runs the containers in detached mode (in the background). You can check the status of your containers with `docker ps` and view logs with `docker logs [container_name]`.

Scaling and Advanced Considerations

While Docker Compose is excellent for development and small-scale deployments, for true production scalability and high availability, you’ll want to move to a more robust orchestration platform.

1. Linode Kubernetes Engine (LKE)

For larger deployments, Linode Kubernetes Engine (LKE) is the natural next step. You would convert your `docker-compose.yml` into Kubernetes manifests (Deployments, Services, StatefulSets, PersistentVolumeClaims).

# Example Kubernetes Deployment for PHP-FPM (simplified)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-fpm-app
spec:
  replicas: 3 # Scale this number
  selector:
    matchLabels:
      app: php-fpm
  template:
    metadata:
      labels:
        app: php-fpm
    spec:
      containers:
      - name: php-fpm
        image: your-dockerhub-repo/php-app:latest # Your built image
        ports:
        - containerPort: 9000
        volumeMounts:
        - name: app-code
          mountPath: /var/www/html
      volumes:
      - name: app-code
        persistentVolumeClaim:
          claimName: php-app-pvc # Assuming a PVC for code if not baked into image

---
# Example Kubernetes Service for PHP-FPM
apiVersion: v1
kind: Service
metadata:
  name: php-fpm-service
spec:
  selector:
    app: php-fpm
  ports:
  - protocol: TCP
    port: 9000
    targetPort: 9000
  type: ClusterIP # Internal service

---
# Example Kubernetes Deployment for Nginx
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-webserver
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:stable-alpine
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: default.conf # Mount specific file
        - name: app-public-assets
          mountPath: /var/www/html/public
      volumes:
      - name: nginx-config
        configMap:
          name: nginx-configmap # A ConfigMap holding nginx.conf
      - name: app-public-assets
        persistentVolumeClaim:
          claimName: php-app-pvc # Assuming same PVC for public assets

---
# Example Kubernetes Service for Nginx (Load Balancer)
apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: LoadBalancer # Linode will provision a Load Balancer

You would also need Kubernetes `StatefulSet` for MySQL and `Deployment` for Redis, along with appropriate `PersistentVolumeClaims` for data persistence. A ConfigMap would hold your `nginx.conf`.

2. Database and Cache Management

For production, consider using managed database services (like Linode’s managed MySQL) or more robust self-hosted solutions. For caching, Redis is a good choice, but ensure you have a strategy for persistence and high availability if needed.

3. CI/CD Pipeline

Automate your build, test, and deployment process. Tools like GitLab CI, GitHub Actions, or Jenkins can be integrated with Linode to automatically build Docker images, push them to a registry (like Docker Hub or Linode Container Registry), and deploy them to your LKE cluster or even update your Docker Compose setup.

4. Monitoring and Logging

Implement comprehensive monitoring (e.g., Prometheus, Grafana) and centralized logging (e.g., ELK stack, Loki) to gain visibility into your application’s performance and health. Linode’s monitoring tools can provide infrastructure-level insights.

By containerizing your legacy Shopify system and orchestrating it on Linode, you gain significant advantages in terms of scalability, reliability, and manageability, paving the way for future modernization efforts.

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

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store
  • How to refactor legacy event ticket registers queries using modern WP_Query and custom Transient caching
  • Step-by-Step Guide: Offloading high-frequency member profile directories metadata writes to a Redis KV store

Categories

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

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • Debugging & Troubleshooting (662)
  • Security & Compliance (647)
  • SEO & Growth (492)
  • 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