• 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 Ruby Systems on Modern DigitalOcean Infrastructure

Dockerizing and Orchestrating Legacy Ruby Systems on Modern DigitalOcean Infrastructure

Assessing Legacy Ruby Application Dependencies

Before embarking on containerization, a thorough audit of the legacy Ruby application’s dependencies is paramount. This involves identifying not only Ruby gems but also system-level libraries, external services, and specific environment variables that the application relies upon. For older Rails applications, this often means dealing with deprecated gems, unsupported Ruby versions, and implicit assumptions about the operating system environment.

A common pitfall is assuming a direct mapping from the existing server environment to a container. Legacy systems might have been installed via package managers (apt, yum) or even compiled from source. These dependencies need to be explicitly declared and managed within the Dockerfile. Tools like bundle outdated and manual inspection of Gemfile.lock are starting points. For system dependencies, consult the application’s deployment documentation or perform runtime analysis.

Crafting the Dockerfile for Ruby Applications

The Dockerfile is the blueprint for your container image. For legacy Ruby applications, it’s crucial to select a base image that supports the required Ruby version. If the application uses an older, unsupported Ruby version (e.g., Ruby 1.8, 1.9), consider using a specific version of an official Ruby image or even a custom-built image based on a compatible OS distribution (like Debian Stable or Ubuntu LTS). For more modern Ruby versions (2.x, 3.x), the official Ruby images are generally sufficient.

Here’s a sample Dockerfile demonstrating best practices for a hypothetical Rails 3.x application:

# Use a base image with a compatible Ruby version.
# For older Ruby versions, you might need to build from source or use a specific OS image.
# Example for Ruby 2.3.x:
FROM ruby:2.3.7-slim

# Set environment variables to prevent interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# Install essential build tools and system dependencies.
# This is highly application-specific. Common examples include:
# - libpq-dev for PostgreSQL
# - libsqlite3-dev for SQLite
# - imagemagick for image processing
# - nodejs and yarn for asset compilation (if not handled separately)
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
    build-essential \
    git \
    libpq-dev \
    libsqlite3-dev \
    imagemagick \
    nodejs \
    yarn \
    && rm -rf /var/lib/apt/lists/*

# Set the working directory inside the container
WORKDIR /app

# Copy the Gemfile and Gemfile.lock first to leverage Docker's layer caching.
# This ensures that if only application code changes, gems are not reinstalled.
COPY Gemfile Gemfile.lock ./

# Install gems. Use a specific bundler version if required by the app.
# Consider using a multi-stage build to reduce final image size by not including build tools.
RUN bundle install --jobs $(nproc) --retry 3

# Copy the rest of the application code
COPY . .

# Precompile assets if using Rails asset pipeline.
# This can be done in a separate stage for smaller production images.
RUN bundle exec rails assets:precompile

# Expose the port the application will run on.
EXPOSE 3000

# Define the command to run the application.
# For development, you might use 'rails server -b 0.0.0.0'.
# For production, consider using a production-ready server like Puma or Unicorn.
# Example using Puma:
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Optimizing for Production: Multi-Stage Builds and Asset Compilation

Production Docker images should be as lean as possible. Multi-stage builds are essential for this. The build stage can install all necessary development tools and compile assets, while the final stage copies only the necessary artifacts from the build stage, resulting in a smaller, more secure image.

# Stage 1: Build stage
FROM ruby:2.3.7-slim AS builder

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
    build-essential \
    git \
    libpq-dev \
    libsqlite3-dev \
    imagemagick \
    nodejs \
    yarn \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install --jobs $(nproc) --retry 3
COPY . .
RUN bundle exec rails assets:precompile

# Stage 2: Production stage
FROM ruby:2.3.7-slim

# Install only runtime dependencies.
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
    libpq5 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
# Copy gems from the builder stage
COPY --from=builder /usr/local/bundle /usr/local/bundle
# Copy application code and precompiled assets from the builder stage
COPY --from=builder /app /app

EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Orchestration with Docker Compose on DigitalOcean

For orchestrating multiple services (e.g., your Ruby app, a database, Redis, Nginx), Docker Compose is an excellent choice, especially for smaller to medium-sized deployments or development environments. DigitalOcean’s Managed Databases and App Platform offer integrated solutions, but for maximum control, running Docker Compose on a DigitalOcean Droplet is a common pattern.

First, ensure Docker and Docker Compose are installed on your DigitalOcean Droplet. You can typically install Docker CE from the official Docker repository and Docker Compose via its release page.

# Install Docker (example for Ubuntu)
sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.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 docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Verify installation
docker --version
docker compose version

Next, create a docker-compose.yml file to define your services. This example assumes a Rails app, PostgreSQL, and Redis.

version: '3.8'

services:
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      POSTGRES_USER: &user your_db_user
      POSTGRES_PASSWORD: &password your_db_password
      POSTGRES_DB: &dbname your_db_name
    ports:
      - "5432:5432" # Only for local development/debugging, remove for production

  redis:
    image: redis:6
    ports:
      - "6379:6379" # Only for local development/debugging, remove for production

  app:
    build: . # Assumes Dockerfile is in the current directory
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/app # Mount application code for development, remove for production
    ports:
      - "3000:3000"
    depends_on:
      - db
      - redis
    environment:
      RAILS_ENV: production
      DATABASE_URL: postgresql://&user:&password@db:5432/&dbname
      REDIS_URL: redis://redis:6379/0
      # Add any other necessary environment variables

volumes:
  postgres_data:

To run this, navigate to the directory containing your Dockerfile and docker-compose.yml and execute:

docker compose up -d

For production deployments on DigitalOcean, you would typically build the Docker image and push it to a container registry (like Docker Hub or DigitalOcean’s Container Registry). Then, your docker-compose.yml would reference the image from the registry instead of using build: .. You would also remove volume mounts for application code and potentially expose ports only to internal networks or via a load balancer.

Leveraging DigitalOcean App Platform for Managed Containerization

DigitalOcean’s App Platform offers a higher level of abstraction, simplifying the deployment and management of containerized applications. It can build and deploy directly from a Git repository or from a container registry. For legacy Ruby apps, this means you can point the App Platform to your Git repo, and it will automatically detect the Ruby application (or you can specify it), build the Docker image (using its internal buildpacks or your provided Dockerfile), and deploy it.

When using App Platform:

  • Build Source: Choose between Git repository or Container Registry. For legacy apps, Git is often simpler if you’re already versioning your code there.
  • Build Command: If App Platform’s auto-detection fails or you need specific build steps (like bundle install --without development test), you can specify custom build commands.
  • Dockerfile: You can provide your own Dockerfile for complete control over the image build process. This is often necessary for complex legacy applications with specific system dependencies.
  • Environment Variables: Configure database credentials, API keys, and other settings directly within the App Platform’s environment variable section.
  • Databases: Integrate with DigitalOcean Managed Databases (PostgreSQL, MySQL, Redis) directly through the App Platform’s database add-on feature. This eliminates the need to manage database containers yourself.

The App Platform handles scaling, load balancing, and SSL termination automatically. For legacy applications, ensure your Dockerfile is robust and that all runtime dependencies are correctly installed. The application should listen on 0.0.0.0 and the port specified by the PORT environment variable (which App Platform injects).

# Example modification to Dockerfile for App Platform
# ... (previous build stage if using multi-stage)

# Production stage
FROM ruby:2.3.7-slim

# Install only runtime dependencies.
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
    libpq5 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
# Copy gems and application code from builder stage if using multi-stage
# COPY --from=builder /usr/local/bundle /usr/local/bundle
# COPY --from=builder /app /app

# If not using multi-stage, copy gems and code here
COPY Gemfile Gemfile.lock ./
RUN bundle install --jobs $(nproc) --retry 3 --without development test

COPY . .

# Precompile assets if needed and not done in a build stage
# RUN bundle exec rails assets:precompile

# App Platform injects the PORT environment variable
EXPOSE $PORT
# Use a production-ready server, listening on 0.0.0.0
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Database Migrations and Background Jobs

Managing database migrations and background job workers requires careful consideration in a containerized environment. For migrations, you can execute them as part of the application startup process, but this can lead to race conditions if multiple instances start simultaneously. A more robust approach is to run migrations as a separate one-off container task.

Using Docker Compose, you can run migrations like this:

docker compose run --rm app bundle exec rails db:migrate

On DigitalOcean App Platform, you can define “Jobs” which are one-off tasks that run to completion. You would configure a migration job that uses your application’s image and executes the migration command.

Background job workers (e.g., Sidekiq, Delayed Job) should be run as separate services in your docker-compose.yml or as separate services within the App Platform. Ensure they are configured to connect to the same Redis or database instance as your main application.

# Example for docker-compose.yml with Sidekiq worker
version: '3.8'

services:
  # ... db, redis, app services ...

  worker:
    build: . # Or reference your app image
    command: bundle exec sidekiq
    volumes:
      - .:/app # Mount application code for development, remove for production
    depends_on:
      - db
      - redis
    environment:
      RAILS_ENV: production
      DATABASE_URL: postgresql://&user:&password@db:5432/&dbname
      REDIS_URL: redis://redis:6379/0
      # Add any other necessary environment variables for the worker

Monitoring and Logging in a Containerized Environment

Effective monitoring and logging are critical for maintaining the health of your containerized legacy applications. Standard tools like Prometheus and Grafana can be deployed to scrape metrics from your application and infrastructure. For logs, consider a centralized logging solution such as the ELK stack (Elasticsearch, Logstash, Kibana) or a cloud-native service like DigitalOcean’s Managed Log Drains.

Ensure your Ruby application logs to stdout and stderr. Docker will capture these streams, and your orchestration platform (Docker Compose or App Platform) can be configured to forward them to your chosen logging backend. For Rails applications, this often means configuring config/environments/production.rb to log to standard output.

# config/environments/production.rb
Rails.application.configure do
  # ... other configurations ...

  # Log to standard output
  config.logger = ActiveSupport::Logger.new(STDOUT)
  config.logger.level = config.log_level

  # Log to standard error for critical errors
  config.middleware.use(
    ActiveSupport::Logger.new(STDERR),
    level: :error
  )

  # ... other configurations ...
end

For metrics, integrate libraries like prometheus-client-mruby or similar for exposing application-level metrics. DigitalOcean Droplets can be monitored using their built-in monitoring tools, and you can install agents for more advanced metrics collection.

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

  • Implementing automated compliance reporting for custom event ticket registers ledgers using TCPDF generator script
  • Step-by-Step Guide: Offloading high-frequency custom product catalogs metadata writes to a Redis KV store
  • Building secure B2B pricing grids with custom Metadata API (add_post_meta) endpoints and role overrides
  • How to securely integrate Algolia Search API endpoints into WordPress custom plugins using WordPress Database Class ($wpdb)
  • How to build custom Understrap styling structures extensions utilizing modern WordPress Settings API schemas

Categories

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

Recent Posts

  • Implementing automated compliance reporting for custom event ticket registers ledgers using TCPDF generator script
  • Step-by-Step Guide: Offloading high-frequency custom product catalogs metadata writes to a Redis KV store
  • Building secure B2B pricing grids with custom Metadata API (add_post_meta) endpoints and role overrides

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (835)
  • Debugging & Troubleshooting (628)
  • Security & Compliance (608)
  • 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