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

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

Terraform Provider Configuration for Linode

To provision infrastructure on Linode using Terraform, we first need to configure the Linode provider. This involves specifying your Linode API token. It’s crucial to manage this token securely, ideally using environment variables or a secrets management system rather than hardcoding it directly into your Terraform configuration.

Create a file named providers.tf in your Terraform project directory. This file will house the provider configuration.

providers.tf

# providers.tf

terraform {
  required_providers {
    linode = {
      source  = "linode/linode"
      version = "~> 1.20" # Specify a version constraint
    }
  }
}

# Configure the Linode provider
# It's recommended to set LINODE_TOKEN environment variable
# export LINODE_TOKEN="your_linode_api_token"
provider "linode" {
  # token = var.linode_api_token # Alternatively, use a variable
}

# Optional: Define a variable for the token if not using environment variables
# variable "linode_api_token" {
#   description = "Linode API Token"
#   type        = string
#   sensitive   = true
# }

After creating this file, you’ll need to initialize Terraform. Navigate to your project directory in your terminal and run:

terraform init

Defining the Ruby Cluster Resources

Now, let’s define the core infrastructure for our Ruby cluster. This will include a VPC network for private communication, a firewall for security, and multiple Linode instances that will host our Ruby applications. We’ll aim for a highly available setup with at least two application servers and a separate database server.

main.tf – VPC, Firewall, and Instances

# main.tf

# Define the Linode VPC network
resource "linode_vpc" "ruby_cluster_vpc" {
  region = "us-east" # Choose your preferred region
  label  = "ruby-cluster-vpc"
  description = "VPC for the Ruby cluster"
}

# Define a firewall for the VPC
resource "linode_firewall" "ruby_cluster_firewall" {
  label       = "ruby-cluster-firewall"
  description = "Firewall for Ruby cluster instances"
  ipv4 {
    inbound {
      label    = "Allow SSH"
      protocol = "TCP"
      ports    = ["22"]
      any_in = true
    }
    inbound {
      label    = "Allow HTTP"
      protocol = "TCP"
      ports    = ["80"]
      any_in = true
    }
    inbound {
      label    = "Allow HTTPS"
      protocol = "TCP"
      ports    = ["443"]
      any_in = true
    }
    # Add rules for inter-instance communication if needed, e.g., for database connections
    inbound {
      label    = "Allow App to DB"
      protocol = "TCP"
      ports    = ["5432"] # PostgreSQL default port
      from_addresses = [linode_instance.app_server.*.private_ip] # Allow from app servers
    }
    outbound {
      label    = "Allow All Outbound"
      protocol = "ALL"
      any_in = true
      any_out = true
    }
  }
  # Associate firewall with the VPC
  # Note: Linode provider v1.20+ allows direct association with VPC
  # For older versions, you might need to associate with individual instances.
  # This example assumes a recent provider version.
  vpc_id = linode_vpc.ruby_cluster_vpc.id
}

# Define the database server instance
resource "linode_instance" "db_server" {
  region   = linode_vpc.ruby_cluster_vpc.region
  type     = "g6-nanode-1" # Adjust instance type as needed
  label    = "ruby-cluster-db"
  image    = "debian 12" # Or your preferred OS image
  root_pass = "your_strong_db_password" # **CHANGE THIS** or use secrets management
  private_ip = true # Enable private IP for VPC communication
  vpc_id = linode_vpc.ruby_cluster_vpc.id
  tags = ["database", "ruby-cluster"]

  # Associate firewall with the instance
  firewall_id = linode_firewall.ruby_cluster_firewall.id

  # User data for initial setup (e.g., installing PostgreSQL)
  user_data = file("scripts/db_setup.sh")
}

# Define the first application server instance
resource "linode_instance" "app_server" {
  count    = 2 # Create two application servers for redundancy
  region   = linode_vpc.ruby_cluster_vpc.region
  type     = "g6-nanode-1" # Adjust instance type as needed
  label    = "ruby-cluster-app-${count.index}"
  image    = "debian 12" # Or your preferred OS image
  root_pass = "your_strong_app_password" # **CHANGE THIS** or use secrets management
  private_ip = true # Enable private IP for VPC communication
  vpc_id = linode_vpc.ruby_cluster_vpc.id
  tags = ["application", "ruby-cluster"]

  # Associate firewall with the instance
  firewall_id = linode_firewall.ruby_cluster_firewall.id

  # User data for initial setup (e.g., installing Ruby, Nginx, Passenger)
  user_data = file("scripts/app_setup.sh")

  # Ensure DB is provisioned before app servers start their setup
  depends_on = [linode_instance.db_server]
}

# Output private IPs for reference
output "db_server_private_ip" {
  description = "Private IP address of the database server"
  value       = linode_instance.db_server.private_ip
}

output "app_server_private_ips" {
  description = "Private IP addresses of the application servers"
  value       = linode_instance.app_server.*.private_ip
}

Instance Setup Scripts

The user_data argument in the linode_instance resource allows us to execute scripts upon instance creation. These scripts are crucial for bootstrapping our servers with the necessary software and configurations.

scripts/db_setup.sh

#!/bin/bash
# scripts/db_setup.sh

# Update package list and install PostgreSQL
apt-get update -y
apt-get install -y postgresql postgresql-contrib

# Configure PostgreSQL for remote access (adjust as per your security needs)
# This is a basic example; for production, use more restrictive settings.
echo "listen_addresses = '*'" >> /etc/postgresql/15/main/postgresql.conf # Adjust version if needed
echo "host    all             all             0.0.0.0/0               md5" >> /etc/postgresql/15/main/pg_hba.conf

# Restart PostgreSQL service
systemctl restart postgresql

# Create a database user and database for the Ruby application
# **IMPORTANT**: Replace 'your_app_user' and 'your_app_password' with strong credentials.
# Consider using a secrets manager for passwords.
sudo -u postgres psql -c "CREATE USER your_app_user WITH PASSWORD 'your_app_password';"
sudo -u postgres psql -c "CREATE DATABASE your_app_db OWNER your_app_user;"

echo "Database server setup complete."

scripts/app_setup.sh

#!/bin/bash
# scripts/app_setup.sh

# Update package list and install essential packages
apt-get update -y
apt-get install -y curl git nginx build-essential libssl-dev libreadline-dev zlib1g-dev

# Install Ruby using rbenv (or another version manager)
# This is a robust way to manage Ruby versions.
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/main/bin/rbenv-installer | bash

# Add rbenv to PATH for the current session and for future logins
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc

# Install Ruby (e.g., latest stable version)
rbenv install 3.2.2 # Replace with your desired Ruby version
rbenv global 3.2.2
gem install bundler --no-document

# Install Passenger for Nginx
gem install passenger --no-document
passenger-install-nginx-module --auto --prefix=/opt/nginx --nginx-dir=/opt/nginx --extra-configure-flags="--with-compat"

# Configure Nginx to use Passenger
# This is a simplified configuration. You'll likely need to customize it
# for your specific Ruby application (e.g., setting root, app_env, etc.).
NGINX_CONF="/etc/nginx/nginx.conf"
PASSENGER_ROOT=$(passenger --root)
PASSENGER_MODULE=$(find /opt/nginx/modules -name "ngx_http_passenger_module.so" | head -n 1)

sed -i "/http {/ i \    load_module modules/ngx_http_passenger_module.so;\n    passenger_root ${PASSENGER_ROOT};\n    passenger_ruby $(rbenv prefix)/bin/ruby;" ${NGINX_CONF}

# Create a basic Nginx site configuration for the Ruby app
APP_DOMAIN="your_app_domain.com" # **CHANGE THIS**
APP_ROOT="/var/www/your_ruby_app" # **CHANGE THIS**

mkdir -p ${APP_ROOT}

cat > /etc/nginx/sites-available/ruby_app <<EOF
server {
    listen 80;
    server_name ${APP_DOMAIN};
    root ${APP_ROOT}/public; # Assuming your Rails app has a public directory

    passenger_enabled on;
    passenger_app_env production; # Or development, staging
    passenger_base_uri /; # Adjust if your app is mounted under a sub-path
    passenger_ruby $(rbenv prefix)/bin/ruby;
}
EOF

# Enable the site and remove default
ln -s /etc/nginx/sites-available/ruby_app /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default

# Test Nginx configuration and restart
nginx -t
systemctl restart nginx

echo "Application server setup complete."
echo "Remember to deploy your Ruby application to ${APP_ROOT} and configure your database connection."

Deployment and Management

With the Terraform configuration in place, you can provision your infrastructure. Ensure you have the Linode CLI installed and authenticated, or that the LINODE_TOKEN environment variable is set.

Terraform Workflow

# Initialize Terraform (if not already done)
terraform init

# Review the execution plan
terraform plan

# Apply the configuration to create resources
terraform apply

# To destroy the infrastructure when no longer needed
# terraform destroy

After running terraform apply, Terraform will create the VPC, firewall, database server, and application servers on Linode. The user_data scripts will execute, setting up PostgreSQL on the database server and Ruby, Nginx, and Passenger on the application servers. The output variables will provide the private IP addresses, which you can use to configure your application’s database connection strings.

Security Considerations and Next Steps

This setup provides a foundational secure environment. However, several critical security aspects need further attention for production deployments:

  • Database Passwords: Never hardcode database passwords. Use Linode Secrets, HashiCorp Vault, or environment variables injected securely.
  • SSH Key Management: Instead of root_pass, use SSH keys for secure access. You can manage SSH keys via Terraform’s linode_ssh_key resource or by pre-loading them onto your Linode account.
  • Firewall Rules: The provided firewall rules are basic. Restrict inbound access to only necessary ports and sources. For the database, only allow connections from your application server’s private IP range.
  • Application Deployment: The app_setup.sh script installs the necessary software but doesn’t deploy your application. Implement a CI/CD pipeline (e.g., using GitHub Actions, GitLab CI, Jenkins) to automate application deployments to the specified APP_ROOT.
  • HTTPS: Configure Nginx for HTTPS using Let’s Encrypt or another certificate authority. This can be automated with tools like Certbot.
  • Monitoring and Logging: Integrate robust monitoring and logging solutions for your cluster.
  • Database Backups: Implement a reliable database backup strategy.
  • Instance Hardening: Further harden your Linode instances by disabling root login via SSH, configuring sudo access, and regularly applying security patches.

By leveraging Terraform, you can consistently and reliably provision secure, scalable Ruby clusters on Linode, enabling faster development cycles and more robust deployments.

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