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

Vengala Vinay

Having 9+ Years of Experience in Software Development

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

Dockerizing and Orchestrating Legacy Shopify Systems on Modern DigitalOcean Infrastructure

Deconstructing the Legacy Shopify Monolith for Containerization

Many established Shopify merchants find themselves with a complex, often monolithic, application architecture. This can include custom themes with deeply embedded logic, numerous third-party app integrations that directly modify core Shopify functionality, and potentially even custom backend services that interact with Shopify via its APIs. The goal of containerization is to decouple these components, making them more manageable, scalable, and deployable on modern infrastructure like DigitalOcean.

The first step is a thorough audit of the existing Shopify setup. Identify distinct functional areas that can be isolated. Common candidates include:

  • Frontend Theme: The Liquid templating, JavaScript, and CSS.
  • Custom Backend Services: Any Ruby on Rails, Node.js, or PHP applications that handle order processing, inventory sync, or custom logic.
  • Data Synchronization Agents: Scripts or services responsible for pushing/pulling data to/from external systems (e.g., ERP, CRM).
  • Background Job Processors: For tasks like email sending, image resizing, or complex data transformations.

For each identified component, we’ll aim to create a self-contained Docker image. This process often involves refactoring existing code to remove dependencies on the Shopify environment itself, relying instead on Shopify’s APIs (REST or GraphQL) for data access and manipulation.

Containerizing the Shopify Frontend Theme

While Shopify themes are primarily served by Shopify’s CDN, we can containerize the development and build process. This ensures a consistent build environment and allows for local testing of theme assets before deployment. A typical Dockerfile for a theme build might look like this:

# Dockerfile for Shopify Theme Development & Build
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Install theme build tools (e.g., Shopify CLI, Webpack, Sass)
RUN npm install -g @shopify/cli yarn

# Copy theme files
COPY . /app

# Install dependencies
RUN yarn install

# Example: Build theme assets (adjust command as needed)
# This command would typically be run locally or in a CI/CD pipeline
# RUN shopify theme build

# Expose a port if you're running a local dev server within the container
# EXPOSE 3000

# Default command (e.g., to start a local dev server)
# CMD ["yarn", "dev"]

The key here is that the container provides a reproducible environment for tools like the Shopify CLI, Yarn, and any other build dependencies. The actual theme deployment still happens through Shopify’s platform, but the container ensures the assets are built correctly and consistently.

Containerizing Custom Backend Services

This is where Docker truly shines. Let’s consider a hypothetical Ruby on Rails application that acts as a middleware, syncing inventory levels with an external system and handling custom order fulfillment logic. The Dockerfile would be more involved:

# Dockerfile for Custom Shopify Backend Service (Ruby on Rails)
FROM ruby:3.1-alpine

# Install necessary build dependencies
RUN apk update && apk add --no-cache \
    build-base \
    git \
    postgresql-dev \
    tzdata \
    curl \
    nodejs \
    yarn

# Set working directory
WORKDIR /app

# Copy Gemfile and Gemfile.lock
COPY Gemfile Gemfile.lock ./

# Install gems
RUN bundle install --jobs 4 --retry 3

# Copy application code
COPY . .

# Precompile assets if applicable (e.g., for Rails admin interfaces)
# RUN bundle exec rails assets:precompile

# Set environment variables (e.g., for database connection, Shopify API keys)
ENV RAILS_ENV=production
ENV DATABASE_URL="postgresql://user:password@db:5432/dbname"
ENV SHOPIFY_API_KEY="your_api_key"
ENV SHOPIFY_API_SECRET="your_api_secret"
ENV SHOPIFY_STORE_DOMAIN="your-store.myshopify.com"
ENV SHOPIFY_ACCESS_TOKEN="your_access_token"

# Expose the port the application runs on (e.g., Puma)
EXPOSE 3000

# Command to run the application
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

In this example:

  • We start with a Ruby Alpine base image for a smaller footprint.
  • Essential build tools and runtime dependencies are installed.
  • Gems are installed, and then the application code is copied.
  • Crucially, environment variables are used to inject sensitive credentials and configuration, avoiding hardcoding.
  • The application is exposed on port 3000, assuming a web server like Puma is used.

Orchestrating with Docker Compose on DigitalOcean

Once individual services are containerized, orchestration becomes essential. Docker Compose is an excellent tool for defining and running multi-container Docker applications. For a DigitalOcean deployment, we can leverage DigitalOcean Kubernetes (DOKS) or run Docker Compose directly on Droplets.

Here’s a sample docker-compose.yml for our hypothetical setup:

version: '3.8'

services:
  # Custom Backend Service
  backend_app:
    build:
      context: ./backend_app # Path to the backend_app directory containing Dockerfile
      dockerfile: Dockerfile
    ports:
      - "8080:3000" # Map host port 8080 to container port 3000
    environment:
      RAILS_ENV: production
      DATABASE_URL: postgresql://user:password@db:5432/dbname
      SHOPIFY_API_KEY: ${SHOPIFY_API_KEY} # Use environment variables from .env file
      SHOPIFY_API_SECRET: ${SHOPIFY_API_SECRET}
      SHOPIFY_STORE_DOMAIN: ${SHOPIFY_STORE_DOMAIN}
      SHOPIFY_ACCESS_TOKEN: ${SHOPIFY_ACCESS_TOKEN}
    depends_on:
      - db
    networks:
      - app-network
    restart: unless-stopped

  # PostgreSQL Database
  db:
    image: postgres:14-alpine
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: dbname
    networks:
      - app-network
    restart: unless-stopped

  # Background Job Processor (e.g., Sidekiq for Rails)
  worker:
    build:
      context: ./backend_app # Assuming worker is in the same repo
      dockerfile: Dockerfile.worker # A separate Dockerfile for the worker
    environment:
      RAILS_ENV: production
      DATABASE_URL: postgresql://user:password@db:5432/dbname
      SHOPIFY_API_KEY: ${SHOPIFY_API_KEY}
      SHOPIFY_API_SECRET: ${SHOPIFY_API_SECRET}
      SHOPIFY_STORE_DOMAIN: ${SHOPIFY_STORE_DOMAIN}
      SHOPIFY_ACCESS_TOKEN: ${SHOPIFY_ACCESS_TOKEN}
    depends_on:
      - db
    networks:
      - app-network
    restart: unless-stopped

  # Redis for background jobs (e.g., Sidekiq)
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network
    restart: unless-stopped

volumes:
  db_data:

networks:
  app-network:
    driver: bridge

To manage secrets like API keys and database passwords, a .env file should be used alongside docker-compose.yml. This file is not committed to version control.

# .env file
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret
SHOPIFY_STORE_DOMAIN=your-store.myshopify.com
SHOPIFY_ACCESS_TOKEN=your_access_token

On a DigitalOcean Droplet, you would typically SSH into the server, navigate to your project directory, and run:

# Ensure Docker and Docker Compose are installed on the Droplet
# https://docs.digitalocean.com/products/droplets/how-to/install-docker/

# Navigate to your project directory
cd /path/to/your/shopify_docker_project

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

# To view logs
docker-compose logs -f

# To stop the services
docker-compose down

Leveraging DigitalOcean Kubernetes (DOKS) for Scalability

For production environments requiring high availability and scalability, DOKS is the preferred choice. The transition from Docker Compose to Kubernetes involves defining Kubernetes manifests (Deployments, Services, StatefulSets, etc.) instead of a docker-compose.yml file. This is a more complex undertaking but offers significant advantages.

Here’s a simplified example of a Kubernetes Deployment for the backend application:

# backend-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-app-deployment
  labels:
    app: backend-app
spec:
  replicas: 3 # Start with 3 replicas for HA
  selector:
    matchLabels:
      app: backend-app
  template:
    metadata:
      labels:
        app: backend-app
    spec:
      containers:
      - name: backend-app
        image: your-docker-registry/backend-app:latest # Replace with your image
        ports:
        - containerPort: 3000
        env:
        - name: RAILS_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: database_url
        - name: SHOPIFY_API_KEY
          valueFrom:
            secretKeyRef:
              name: shopify-credentials
              key: api_key
        - name: SHOPIFY_API_SECRET
          valueFrom:
            secretKeyRef:
              name: shopify-credentials
              key: api_secret
        - name: SHOPIFY_STORE_DOMAIN
          valueFrom:
            secretKeyRef:
              name: shopify-credentials
              key: store_domain
        - name: SHOPIFY_ACCESS_TOKEN
          valueFrom:
            secretKeyRef:
              name: shopify-credentials
              key: access_token
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health # Assuming a health check endpoint
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 20
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 10
      # Define volumes for persistent storage if needed (e.g., for logs)
      # volumes:
      # - name: log-volume
      #   emptyDir: {}

And a corresponding Kubernetes Service to expose the deployment:

# backend-app-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend-app-service
spec:
  selector:
    app: backend-app
  ports:
    - protocol: TCP
      port: 80 # The port the service listens on within the cluster
      targetPort: 3000 # The port the container listens on
  type: LoadBalancer # DigitalOcean will provision a Load Balancer for this

Secrets management in Kubernetes is handled via Kubernetes Secrets. These would be created separately and referenced in the Deployment. The process on DOKS involves:

  • Configuring kubectl to connect to your DOKS cluster.
  • Creating Kubernetes Secrets for sensitive information.
  • Applying the Deployment and Service manifests using kubectl apply -f <filename>.
  • For persistent storage (like PostgreSQL), a StatefulSet and PersistentVolumeClaim would be used, often leveraging DigitalOcean’s Block Storage.

Database and Persistent Storage Considerations

For stateful services like databases (PostgreSQL, Redis), using Docker volumes with Docker Compose is a good start. However, for production on DOKS, it’s highly recommended to use managed database services (like DigitalOcean Managed Databases) or Kubernetes StatefulSets with PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs) backed by DigitalOcean Block Storage. This ensures data durability and easier management.

When using Docker Compose, the volumes: db_data:/var/lib/postgresql/data directive ensures data persists even if the container is removed and recreated. On DOKS, a StatefulSet would manage the lifecycle of the database pods and their associated persistent storage.

Monitoring and Logging

A robust monitoring and logging strategy is critical. For Docker Compose deployments on Droplets, you can:

  • Use docker logs to view container output.
  • Implement centralized logging by forwarding logs to a service like Logtail or Elasticsearch/Kibana running on another Droplet or as a managed service.
  • Install monitoring agents (e.g., Prometheus Node Exporter, cAdvisor) on the Droplet to collect system and container metrics.

On DOKS, you can deploy a full monitoring stack like Prometheus and Grafana, or leverage DigitalOcean’s integrated monitoring capabilities. Log aggregation is typically handled by deploying a logging agent (like Fluentd or Filebeat) as a DaemonSet to collect logs from all nodes and forward them to a central store.

CI/CD Pipeline Integration

Automating the build, test, and deployment process is key to leveraging containerization effectively. A typical CI/CD pipeline using GitHub Actions or GitLab CI would:

  • Trigger on code commits to your repository.
  • Build Docker images for each service.
  • Push images to a container registry (e.g., DigitalOcean Container Registry, Docker Hub, AWS ECR).
  • For Docker Compose: SSH into Droplets and run docker-compose pull && docker-compose up -d.
  • For DOKS: Update Kubernetes Deployments to use the new image tag, triggering rolling updates.

This ensures that changes are deployed quickly, reliably, and with minimal manual intervention, transforming the management of legacy Shopify systems into a modern, cloud-native workflow.

Primary Sidebar

A little about the Author

Having 9+ Years of Experience in Software Development.
Expertised in Php Development, WordPress Custom Theme Development (From scratch using underscores or Genesis Framework or using any blank theme or Premium Theme), Custom Plugin Development. Hands on Experience on 3rd Party Php Extension like Chilkat, nSoftware.

Recent Posts

  • Step-by-Step: Diagnosing indexing lock conflicts and high CPU during bulk stock updates on DigitalOcean Servers
  • How to Debug and Fix memory leaks and socket exhaustion in daemon processes in Modern C++ Applications
  • Infrastructure as Code: Provisioning Secure PHP Clusters on DigitalOcean Using Terraform
  • Fixing Slow Largest Contentful Paint (LCP) caused by unoptimized database queries in Legacy Laravel Codebases Without Breaking API Contracts
  • An Auditor’s Checklist for Securing Laravel Backends on Google Cloud

Copyright © 2026 · Vinay Vengala