• 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 » Infrastructure as Code: Provisioning Secure Ruby Clusters on OVH Using Terraform

Infrastructure as Code: Provisioning Secure Ruby Clusters on OVH Using Terraform

OVHcloud Provider Configuration for Terraform

To provision resources on OVHcloud, we’ll leverage the official Terraform OVHcloud provider. This involves setting up credentials and specifying the region. For security, it’s highly recommended to use environment variables or a dedicated Terraform variables file rather than hardcoding sensitive information directly in your configuration.

First, ensure you have the OVHcloud provider block defined in your Terraform configuration. This typically resides in a providers.tf file.

OVHcloud Provider Block

# providers.tf

terraform {
  required_providers {
    ovh = {
      source  = "ovh/ovh"
      version = "~> 1.0" # Specify a version constraint
    }
  }
}

provider "ovh" {
  endpoint = "ovh-eu" # Or "ovh-us", "ovh-ca"
  # Credentials can be provided via environment variables:
  # OVH_APPLICATION_KEY
  # OVH_APPLICATION_SECRET
  # OVH_CONSUMER_KEY
  # OVH_ENDPOINT (optional, if not set in endpoint argument)
}

You’ll need to obtain your OVHcloud API credentials. This involves creating an application within the OVHcloud control panel. The key components are:

  • Application Key: Your primary API key.
  • Application Secret: A secret associated with your key.
  • Consumer Key: A key generated after authorizing your application.

These credentials should be set as environment variables before running Terraform commands:

Setting Environment Variables

export OVH_APPLICATION_KEY="your_application_key"
export OVH_APPLICATION_SECRET="your_application_secret"
export OVH_CONSUMER_KEY="your_consumer_key"
# export OVH_ENDPOINT="ovh-eu" # If not specified in provider block

With the provider configured and credentials set, we can proceed to define the infrastructure for our Ruby cluster.

Defining the Ruby Cluster Infrastructure

Our Ruby cluster will consist of several components: a public cloud project, a private network, and multiple instances to host the Ruby application. We’ll also set up security groups to control network access.

Terraform Configuration for the Cluster

# main.tf

# --- Project and Network ---

resource "ovh_cloud_project" "ruby_project" {
  service_name = "ruby-cluster-project"
  region       = "GRA1" # Example region, choose your preferred one
  description  = "Project for hosting Ruby cluster"
}

resource "ovh_cloud_network_private" "ruby_network" {
  service_name = ovh_cloud_project.ruby_project.service_name
  name         = "ruby-cluster-vpc"
  region       = ovh_cloud_project.ruby_project.region
  cidr         = "10.0.0.0/16"
  description  = "Private network for Ruby cluster"
}

# --- Security Groups ---

resource "ovh_cloud_network_private_subnet" "ruby_subnet" {
  service_name = ovh_cloud_project.ruby_network.service_name
  network_id   = ovh_cloud_network_private.ruby_network.network_id
  region       = ovh_cloud_project.ruby_network.region
  cidr         = "10.0.1.0/24"
  name         = "ruby-cluster-subnet"
  description  = "Subnet for Ruby cluster instances"
}

resource "ovh_cloud_securitygroup" "ruby_sg" {
  service_name = ovh_cloud_project.ruby_project.service_name
  region       = ovh_cloud_project.ruby_project.region
  name         = "ruby-cluster-sg"
  description  = "Security group for Ruby cluster"
}

# Allow SSH access from a specific IP range (e.g., your office IP)
resource "ovh_cloud_securitygroup_rule" "ssh_ingress" {
  service_name  = ovh_cloud_securitygroup.ruby_sg.service_name
  security_group_id = ovh_cloud_securitygroup.ruby_sg.id
  region        = ovh_cloud_securitygroup.ruby_sg.region
  protocol      = "tcp"
  port_in_range = 22
  cidr          = "YOUR_OFFICE_IP/32" # IMPORTANT: Replace with your actual IP
  direction     = "in"
  description   = "Allow SSH access"
}

# Allow HTTP access from anywhere
resource "ovh_cloud_securitygroup_rule" "http_ingress" {
  service_name  = ovh_cloud_securitygroup.ruby_sg.service_name
  security_group_id = ovh_cloud_securitygroup.ruby_sg.id
  region        = ovh_cloud_securitygroup.ruby_sg.region
  protocol      = "tcp"
  port_in_range = 80
  cidr          = "0.0.0.0/0"
  direction     = "in"
  description   = "Allow HTTP access"
}

# Allow HTTPS access from anywhere
resource "ovh_cloud_securitygroup_rule" "https_ingress" {
  service_name  = ovh_cloud_securitygroup.ruby_sg.service_name
  security_group_id = ovh_cloud_securitygroup.ruby_sg.id
  region        = ovh_cloud_securitygroup.ruby_sg.region
  protocol      = "tcp"
  port_in_range = 443
  cidr          = "0.0.0.0/0"
  direction     = "in"
  description   = "Allow HTTPS access"
}

# Allow all outbound traffic (adjust if stricter egress control is needed)
resource "ovh_cloud_securitygroup_rule" "all_egress" {
  service_name  = ovh_cloud_securitygroup.ruby_sg.service_name
  security_group_id = ovh_cloud_securitygroup.ruby_sg.id
  region        = ovh_cloud_securitygroup.ruby_sg.region
  protocol      = "any"
  cidr          = "0.0.0.0/0"
  direction     = "out"
  description   = "Allow all outbound traffic"
}

# --- Instances ---

# Define instance variables for easier management
variable "instance_count" {
  description = "Number of Ruby application instances"
  type        = number
  default     = 3
}

variable "instance_flavor" {
  description = "Flavor for the Ruby instances (e.g., 'b2-7', 'c2-15')"
  type        = string
  default     = "b2-7" # Example: 2 vCPU, 7 GB RAM
}

variable "instance_image" {
  description = "Image ID for the Ruby instances (e.g., Ubuntu 22.04 LTS)"
  type        = string
  default     = "ubuntu-2204-jammy-amd64" # Find latest image IDs in OVHcloud console or via API
}

resource "ovh_cloud_instance" "ruby_app_instance" {
  count = var.instance_count

  service_name = ovh_cloud_project.ruby_project.service_name
  region       = ovh_cloud_project.ruby_project.region
  name         = "ruby-app-${count.index + 1}"
  flavor_name  = var.instance_flavor
  image_id     = var.instance_image
  ssh_key_name = "my-terraform-ssh-key" # IMPORTANT: Ensure this SSH key is uploaded to your OVHcloud account
  public_cloud_network_uuid = ovh_cloud_network_private.ruby_network.network_id # Connect to the private network
  security_group_ids = [ovh_cloud_securitygroup.ruby_sg.id]

  user_data = templatefile("${path.module}/scripts/bootstrap.sh.tpl", {
    app_name = "my-ruby-app"
    # Add any other variables needed for bootstrapping
  })

  lifecycle {
    create_before_destroy = true
  }
}

# --- Outputs ---

output "ruby_instance_ips" {
  description = "Public IPs of the Ruby application instances"
  value       = [for instance in ovh_cloud_instance.ruby_app_instance : instance.public_ip]
}

output "ruby_private_ips" {
  description = "Private IPs of the Ruby application instances"
  value       = [for instance in ovh_cloud_instance.ruby_app_instance : instance.private_ip]
}

In the above configuration:

  • We define a ovh_cloud_project to logically group our resources.
  • A private network (ovh_cloud_network_private) and subnet (ovh_cloud_network_private_subnet) are created for internal communication.
  • A security group (ovh_cloud_securitygroup) is established with specific ingress rules for SSH (from a trusted IP), HTTP, and HTTPS. Egress is set to allow all outbound traffic, which is common but can be restricted further for enhanced security.
  • We use variables (instance_count, instance_flavor, instance_image) to make the instance configuration flexible.
  • Multiple instances (ovh_cloud_instance) are provisioned, connected to the private network, and assigned the security group.
  • Crucially, ssh_key_name refers to an SSH key pair that must be pre-uploaded to your OVHcloud account. Terraform will inject the public key for access.
  • The user_data parameter uses a template file (scripts/bootstrap.sh.tpl) to execute a shell script on instance boot. This is essential for installing Ruby, dependencies, and deploying your application.

Instance Bootstrapping with User Data

The user_data script is a critical part of automating the setup of your Ruby application on each instance. This script runs as root on first boot.

# scripts/bootstrap.sh.tpl
#!/bin/bash -xe

# Update package lists and install essential packages
apt-get update -y
apt-get upgrade -y
apt-get install -y \
    git \
    curl \
    wget \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libreadline-dev \
    libyaml-dev \
    libsqlite3-dev \
    sqlite3 \
    libxml2-dev \
    libxslt1-dev \
    libcurl4-openssl-dev \
    software-properties-common \
    libffi-dev \
    nginx \
    ufw

# Install Ruby using rbenv for version management
# This is a robust way to manage Ruby versions and gems
export PATH="$HOME/.rbenv/bin:$PATH"
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/main/bin/rbenv-installer | bash

# Configure rbenv to load automatically
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc

# Install a specific Ruby version (e.g., 3.2.2)
rbenv install 3.2.2
rbenv global 3.2.2
gem install bundler --no-document

# Configure Nginx as a reverse proxy
# Assuming your Ruby app runs on port 3000
NGINX_CONF="/etc/nginx/sites-available/ruby_app"
cat <<EOF > $NGINX_CONF
server {
    listen 80 default_server;
    server_name _; # Catch all hostnames

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }
}
EOF

# Enable the Nginx site and restart Nginx
ln -sf $NGINX_CONF /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default # Remove default Nginx site
nginx -t # Test Nginx configuration
systemctl restart nginx

# Configure Firewall (UFW)
ufw allow ssh
ufw allow http
ufw allow https
ufw allow 3000 # Allow traffic to the Ruby app port if direct access is needed for debugging
ufw --force enable

# --- Application Deployment Placeholder ---
# This section is a placeholder. You would typically:
# 1. Clone your application repository.
# 2. Install gems using 'bundle install'.
# 3. Configure your application (e.g., database connection strings).
# 4. Start your Ruby application using a process manager like Puma or Unicorn.
# Example (conceptual):
# cd /var/www/
# git clone YOUR_APP_REPO app_name
# cd app_name
# bundle install --without development test
# # Configure database.yml, secrets.yml etc.
# # Start Puma/Unicorn (e.g., using systemd service)
# systemctl start my-ruby-app.service
# -----------------------------------------

echo "Ruby cluster instance bootstrapping complete."

This script:

  • Updates the system and installs necessary build tools, Git, Nginx, and UFW.
  • Installs rbenv and a specific Ruby version (e.g., 3.2.2), along with Bundler. This is crucial for reproducible Ruby environments.
  • Configures Nginx as a reverse proxy to forward requests from port 80 to your Ruby application running on port 3000.
  • Sets up UFW (Uncomplicated Firewall) to allow SSH, HTTP, and HTTPS traffic.
  • Includes a placeholder for your actual application deployment steps (cloning the repo, installing gems, starting the application server).

Important Considerations for User Data:

  • Idempotency: Ensure your scripts are idempotent, meaning they can be run multiple times without unintended side effects.
  • Secrets Management: Do NOT hardcode sensitive information (database passwords, API keys) directly in the user data script. Use a secrets management solution (like HashiCorp Vault, AWS Secrets Manager, or OVHcloud’s own secrets service if available) and fetch them during runtime or inject them securely.
  • Process Management: For production, use a process manager like systemd, supervisord, Puma, or Unicorn to ensure your Ruby application runs reliably, restarts on failure, and is managed effectively.
  • Image Selection: Always use up-to-date and secure base images. Verify the image ID for your chosen region and OS.

Terraform Workflow

Once your Terraform files (providers.tf, main.tf, and the scripts/bootstrap.sh.tpl file) are in place, and your OVHcloud credentials are set as environment variables, you can provision your infrastructure:

Initialization

terraform init

This command downloads the OVHcloud provider plugin.

Plan and Review

terraform plan

This command shows you what Terraform will create, modify, or destroy. Carefully review the plan to ensure it matches your expectations.

Apply

terraform apply

Terraform will prompt you to confirm the changes. Type yes to proceed with provisioning the resources on OVHcloud. After the apply is complete, the output will display the public and private IP addresses of your Ruby instances.

Destroy

terraform destroy

When you no longer need the infrastructure, this command will tear down all provisioned resources. Use with caution.

Security Best Practices and Next Steps

This setup provides a foundational secure Ruby cluster. For production environments, consider the following:

  • SSH Key Management: Use a dedicated SSH key for Terraform and manage instance access keys securely. Rotate keys regularly.
  • Secrets Management: Implement a robust secrets management solution for database credentials, API keys, and application secrets.
  • Database Provisioning: Provision a managed database service (e.g., OVHcloud Managed Databases for PostgreSQL or MySQL) instead of running databases on application instances.
  • Load Balancing: For high availability and scalability, introduce a load balancer (e.g., OVHcloud Load Balancer) in front of your Ruby instances.
  • Monitoring and Logging: Integrate monitoring tools (Prometheus, Grafana) and centralized logging solutions (ELK stack, Loki) for visibility into your cluster’s health and performance.
  • CI/CD Integration: Integrate Terraform into your CI/CD pipeline for automated infrastructure deployments and updates.
  • Immutable Infrastructure: For true scalability and reliability, consider building custom machine images with your application pre-installed, rather than relying solely on user data for bootstrapping.
  • Network Segmentation: Further segment your network if you have different tiers of applications or services.

By leveraging Infrastructure as Code with Terraform and adhering to security best practices, you can efficiently and reliably provision and manage your Ruby clusters on OVHcloud, enabling scalable and resilient deployments.

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 thread pools deadlock during concurrent ActiveRecord transaction processing on Linode Servers
  • Securing Your E-commerce APIs: Preventing SQL Injection (SQLi) in customized checkout queries in WooCommerce Implementations
  • Disaster Recovery 101: Architecting Auto-Failovers for MySQL and Ruby Deployments on Linode
  • High-Throughput Caching Strategies: Scaling MySQL for Perl Application APIs
  • Disaster Recovery 101: Architecting Auto-Failovers for DynamoDB and Laravel Deployments on DigitalOcean

Copyright © 2026 · Vinay Vengala