• 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 Magento 2 Clusters on OVH Using Terraform

Infrastructure as Code: Provisioning Secure Magento 2 Clusters on OVH Using Terraform

OVH Provider Configuration for Terraform

To provision resources on OVHcloud using Terraform, we first need to configure the OVH provider. This involves specifying your OVH API credentials and the region where you intend to deploy your Magento 2 cluster. It’s crucial to manage these credentials securely, ideally using environment variables or a dedicated secrets management system rather than hardcoding them directly into your Terraform configuration.

The OVH provider requires your Application Key, Application Secret, Consumer Key, and the endpoint for the OVH API. For production environments, it’s highly recommended to use a dedicated service user with restricted permissions.

Terraform Configuration File (provider.tf)

# provider.tf

terraform {
  required_providers {
    ovh = {
      source  = "ovh/ovh"
      version = "~> 2.0" # Pin to a specific major version for stability
    }
  }
}

provider "ovh" {
  # It is strongly recommended to use environment variables for sensitive credentials.
  # export OVH_APPLICATION_KEY="YOUR_APP_KEY"
  # export OVH_APPLICATION_SECRET="YOUR_APP_SECRET"
  # export OVH_CONSUMER_KEY="YOUR_CONSUMER_KEY"
  # export OVH_ENDPOINT="ovh-eu" # or "ovh-us", "ovh-ca"

  application_key    = var.ovh_application_key
  application_secret = var.ovh_application_secret
  consumer_key       = var.ovh_consumer_key
  endpoint           = var.ovh_endpoint
}

variable "ovh_application_key" {
  description = "OVH Application Key for API access."
  type        = string
  sensitive   = true
}

variable "ovh_application_secret" {
  description = "OVH Application Secret for API access."
  type        = string
  sensitive   = true
}

variable "ovh_consumer_key" {
  description = "OVH Consumer Key for API access."
  type        = string
  sensitive   = true
}

variable "ovh_endpoint" {
  description = "OVH API endpoint (e.g., ovh-eu, ovh-us, ovh-ca)."
  type        = string
  default     = "ovh-eu"
}

To initialize Terraform with this configuration, you would typically run:

terraform init

And then apply the configuration, ensuring your environment variables are set:

export OVH_APPLICATION_KEY="your_app_key_here"
export OVH_APPLICATION_SECRET="your_app_secret_here"
export OVH_CONSUMER_KEY="your_consumer_key_here"
terraform apply

Networking and Security Groups

A secure Magento 2 deployment necessitates a well-defined network infrastructure. This includes setting up a Virtual Private Cloud (VPC), subnets, and crucially, security groups to control inbound and outbound traffic. For Magento, we need to allow HTTP (80), HTTPS (443), SSH (22), and potentially other ports for database access or management tools.

VPC and Subnet Configuration (network.tf)

# network.tf

resource "ovh_cloud_project_network_private" "magento_vpc" {
  service_name = var.ovh_service_name
  name         = "magento-vpc"
  region       = var.ovh_region
  description  = "VPC for Magento 2 cluster"
}

resource "ovh_cloud_project_network_subnet" "magento_subnet" {
  service_name = var.ovh_service_name
  network_id   = ovh_cloud_project_network_private.magento_vpc.id
  name         = "magento-subnet-public"
  region       = var.ovh_region
  cidr         = "10.0.1.0/24" # Example CIDR block
  gateway      = "10.0.1.254"
  dhcp_enabled = true
  description  = "Public subnet for Magento web servers"
}

# You might want a private subnet for databases or other backend services
resource "ovh_cloud_project_network_subnet" "magento_subnet_private" {
  service_name = var.ovh_service_name
  network_id   = ovh_cloud_project_network_private.magento_vpc.id
  name         = "magento-subnet-private"
  region       = var.ovh_region
  cidr         = "10.0.2.0/24" # Example CIDR block
  gateway      = "10.0.2.254"
  dhcp_enabled = true
  description  = "Private subnet for Magento database servers"
}

variable "ovh_service_name" {
  description = "The name of your OVH cloud project."
  type        = string
}

variable "ovh_region" {
  description = "The OVH region to deploy resources in (e.g., 'GRA1', 'BHS1')."
  type        = string
}

Security Group Rules (security.tf)

# security.tf

resource "ovh_cloud_project_security_group" "magento_sg" {
  service_name = var.ovh_service_name
  name         = "magento-security-group"
  region       = var.ovh_region
  description  = "Security group for Magento 2 cluster"
  network_id   = ovh_cloud_project_network_private.magento_vpc.id
}

# Allow SSH access from a specific IP range (e.g., your office or bastion host)
resource "ovh_cloud_project_security_group_rule" "allow_ssh" {
  service_name    = var.ovh_service_name
  security_group_id = ovh_cloud_project_security_group.magento_sg.id
  region          = var.ovh_region
  protocol        = "tcp"
  port_min        = 22
  port_max        = 22
  cidr            = "YOUR_TRUSTED_IP_RANGE/32" # e.g., "203.0.113.0/24" or "192.168.1.0/24"
  direction       = "ingress"
  description     = "Allow SSH access"
}

# Allow HTTP access from anywhere
resource "ovh_cloud_project_security_group_rule" "allow_http" {
  service_name    = var.ovh_service_name
  security_group_id = ovh_cloud_project_security_group.magento_sg.id
  region          = var.ovh_region
  protocol        = "tcp"
  port_min        = 80
  port_max        = 80
  cidr            = "0.0.0.0/0"
  direction       = "ingress"
  description     = "Allow HTTP access"
}

# Allow HTTPS access from anywhere
resource "ovh_cloud_project_security_group_rule" "allow_https" {
  service_name    = var.ovh_service_name
  security_group_id = ovh_cloud_project_security_group.magento_sg.id
  region          = var.ovh_region
  protocol        = "tcp"
  port_min        = 443
  port_max        = 443
  cidr            = "0.0.0.0/0"
  direction       = "ingress"
  description     = "Allow HTTPS access"
}

# Allow all outbound traffic (common for web servers to fetch updates, etc.)
resource "ovh_cloud_project_security_group_rule" "allow_outbound" {
  service_name    = var.ovh_service_name
  security_group_id = ovh_cloud_project_security_group.magento_sg.id
  region          = var.ovh_region
  protocol        = "any"
  port_min        = 0
  port_max        = 0
  cidr            = "0.0.0.0/0"
  direction       = "egress"
  description     = "Allow all outbound traffic"
}

# You might want to restrict database access to only your web servers' subnet
# resource "ovh_cloud_project_security_group_rule" "allow_db_from_web" {
#   service_name    = var.ovh_service_name
#   security_group_id = ovh_cloud_project_security_group.magento_sg.id
#   region          = var.ovh_region
#   protocol        = "tcp"
#   port_min        = 3306 # Default MySQL port
#   port_max        = 3306
#   cidr            = "10.0.1.0/24" # CIDR of your web server subnet
#   direction       = "ingress"
#   description     = "Allow MySQL access from web subnet"
# }

Important: Replace YOUR_TRUSTED_IP_RANGE/32 with your actual IP address or network range. For production, avoid 0.0.0.0/0 for SSH. Consider using a bastion host or VPN for secure administrative access.

Compute Instances for Magento 2

A typical Magento 2 cluster involves multiple instances: web servers (running Nginx/Apache, PHP-FPM), a database server (MySQL/MariaDB), and potentially caching layers (Redis, Varnish). We’ll provision these using OVH’s Public Cloud Instances.

Web Server Instances (webservers.tf)

# webservers.tf

resource "ovh_cloud_project_instance" "magento_web" {
  count        = var.web_server_count # e.g., 2 for HA
  service_name = var.ovh_service_name
  name         = "magento-web-${count.index}"
  region       = var.ovh_region
  flavor_name  = var.web_server_flavor # e.g., "vps-ssd-2" or a specific instance type
  image_name   = "Debian 11" # Or your preferred OS image
  ssh_key_name = var.ssh_key_name # Ensure this SSH key is uploaded to your OVH project

  # Attach to the public subnet
  network_id = ovh_cloud_project_network_subnet.magento_subnet.id

  # Associate with the security group
  security_group_ids = [ovh_cloud_project_security_group.magento_sg.id]

  # User data for initial setup (e.g., installing Nginx, PHP, Magento prerequisites)
  user_data = base64encode(templatefile("${path.module}/cloud-init/webserver.yaml", {
    db_host = ovh_cloud_project_database.magento_db.private_connection_uri # Assuming DB is in private network
    db_user = var.db_user
    db_password = var.db_password
    magento_root_dir = "/var/www/html/magento"
    # Add other variables needed for cloud-init
  }))

  tags = {
    environment = var.environment
    role        = "magento-web"
  }

  lifecycle {
    create_before_destroy = true
  }
}

variable "web_server_count" {
  description = "Number of web server instances."
  type        = number
  default     = 2
}

variable "web_server_flavor" {
  description = "Flavor name for web server instances."
  type        = string
  default     = "vps-ssd-2" # Example flavor
}

variable "ssh_key_name" {
  description = "Name of the SSH key uploaded to OVH for instance access."
  type        = string
}

variable "environment" {
  description = "Deployment environment (e.g., 'production', 'staging')."
  type        = string
  default     = "production"
}

Database Instance (database.tf)

For a production Magento 2 setup, using a managed database service is highly recommended for reliability and ease of management. OVH offers managed database services (e.g., MySQL, PostgreSQL).

# database.tf

resource "ovh_cloud_project_database" "magento_db" {
  service_name = var.ovh_service_name
  engine       = "mysql"
  version      = "8.0" # Specify desired MySQL version
  plan_name    = "professional-1" # Choose an appropriate plan based on expected load
  region       = var.ovh_region
  name         = "magento-db"
  description  = "Managed MySQL database for Magento 2"

  # Configure network access. For enhanced security, use private network access.
  # Ensure your web servers are in the same VPC or have network connectivity.
  network_type = "private" # or "public" if absolutely necessary, but less secure

  # Optional: Configure backup settings
  # backup_day_of_month = 1
  # backup_hour         = 3

  tags = {
    environment = var.environment
    role        = "magento-db"
  }
}

# Note: Database credentials (username, password) are typically managed
# by the managed service and accessed via its API or console.
# Terraform can retrieve these if the provider supports it, or you might
# need to set them manually or via a separate process.
# For simplicity, we'll assume we can retrieve connection details.

# Example of retrieving connection details (may vary by provider version/features)
# You might need to create a user and grant privileges separately.
# The 'private_connection_uri' is a placeholder and might not be directly available
# without further configuration or specific provider resource.
# A common approach is to use the OVH API or CLI to get connection strings.

# For demonstration, let's assume we can get the hostname and port.
# In a real scenario, you'd likely use a data source or a separate script
# to fetch the full connection details after the database is provisioned.

# Example: Fetching database host and port (hypothetical, check OVH provider docs)
# data "ovh_cloud_project_database_info" "magento_db_info" {
#   service_name = var.ovh_service_name
#   db_id        = ovh_cloud_project_database.magento_db.id
# }

# For now, we'll use variables for DB credentials that will be passed to cloud-init
variable "db_user" {
  description = "Database username for Magento."
  type        = string
  default     = "magento_user"
  sensitive   = true
}

variable "db_password" {
  description = "Database password for Magento."
  type        = string
  sensitive   = true
}

Caching Layer (Optional – Redis)

For performance, Redis is often used for Magento’s caching. You can provision a managed Redis instance or deploy it on a separate compute instance.

# cache.tf (Example for managed Redis)

resource "ovh_cloud_project_database" "magento_redis" {
  service_name = var.ovh_service_name
  engine       = "redis"
  version      = "6.2" # Specify desired Redis version
  plan_name    = "cache-basic" # Choose an appropriate plan
  region       = var.ovh_region
  name         = "magento-redis"
  description  = "Managed Redis cache for Magento 2"
  network_type = "private"

  tags = {
    environment = var.environment
    role        = "magento-cache"
  }
}

# Similar to the database, you'll need to retrieve Redis connection details.
# This might involve fetching the host and port from the OVH API.
# For cloud-init, you'd pass these details.

Cloud-Init for Instance Configuration

Cloud-init is essential for automating the initial setup of your instances. This includes installing software, configuring services, and deploying your Magento application. Below is a conceptual example of a webserver.yaml file.

cloud-init/webserver.yaml Example

# cloud-init/webserver.yaml
#cloud-config
package_update: true
packages:
  - nginx
  - php8.1
  - php8.1-fpm
  - php8.1-mysql
  - php8.1-xml
  - php8.1-mbstring
  - php8.1-zip
  - php8.1-gd
  - php8.1-curl
  - composer
  - git
  - unzip

runcmd:
  # Configure Nginx and PHP-FPM
  - sed -i 's/user = www-data/user = nginx/' /etc/php/8.1/fpm/pool.d/www.conf
  - sed -i 's/group = www-data/group = nginx/' /etc/php/8.1/fpm/pool.d/www.conf
  - sed -i 's/;listen.owner = www-data/;listen.owner = nginx/' /etc/php/8.1/fpm/pool.d/www.conf
  - sed -i 's/;listen.group = www-data/;listen.group = nginx/' /etc/php/8.1/fpm/pool.d/www.conf
  - sed -i 's/;listen.mode = 0660/;listen.mode = 0660/' /etc/php/8.1/fpm/pool.d/www.conf
  - systemctl enable nginx
  - systemctl enable php8.1-fpm
  - systemctl start nginx
  - systemctl start php8.1-fpm

  # Deploy Magento (This is a simplified example. Use a proper deployment strategy)
  - mkdir -p ${magento_root_dir}
  - chown nginx:nginx ${magento_root_dir}
  - cd ${magento_root_dir}
  # - git clone your-magento-repo . # Or use composer create-project
  - composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition .
  - chown -R nginx:nginx ${magento_root_dir}

  # Magento CLI commands (requires database and cache to be ready)
  # These commands are best run AFTER initial provisioning and configuration.
  # Consider running them via a separate script or a deployment pipeline.
  # - php bin/magento setup:install --db-host=${db_host} --db-name=magento_db --db-user=${db_user} --db-password=${db_password} --admin-user=admin --admin-password=your_secure_admin_password [email protected] --base-url=http://your-domain.com
  # - php bin/magento setup:upgrade
  # - php bin/magento cache:enable
  # - php bin/magento deploy:mode:set production

write_files:
  - path: /etc/nginx/sites-available/magento
    permissions: '0644'
    content: |
      server {
          listen 80;
          server_name your-domain.com www.your-domain.com; # Replace with your domain
          root ${magento_root_dir};
          index index.php index.html index.htm;

          location / {
              try_files $uri $uri/ /index.php?$args;
          }

          location ~ ^/index\.php/ {
              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_pass unix:/run/php/php8.1-fpm.sock; # Adjust if using TCP socket
              fastcgi_read_timeout 300; # Increase timeout for Magento operations
          }

          location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
              expires 1y;
              log_not_found off;
              access_log off;
          }

          location /var/ {
              deny all;
          }

          location /app/etc/ {
              deny all;
          }
      }
  - path: /etc/nginx/sites-enabled/magento
    permissions: '0644'
    content: |
      # This is a symlink, content is managed by /etc/nginx/sites-available/magento
      # Ensure the symlink is created correctly.
      # A more robust approach is to use a template file for the symlink.
      # For simplicity, we'll assume the symlink is created manually or via another step.
      # A better cloud-init would handle symlinking:
      # - ln -s /etc/nginx/sites-available/magento /etc/nginx/sites-enabled/magento
      # - rm /etc/nginx/sites-enabled/default # Remove default Nginx site

# Ensure the default Nginx site is removed and the new one is enabled
# This part is tricky with cloud-init's sequential execution.
# It's often better to manage Nginx config via Ansible/Chef/Puppet after initial boot.
# However, for a basic setup:
runcmd:
  - rm -f /etc/nginx/sites-enabled/default
  - ln -s /etc/nginx/sites-available/magento /etc/nginx/sites-enabled/magento
  - systemctl reload nginx

# Configure firewall (ufw example, adjust if using iptables directly)
# This assumes ufw is installed.
# - apt-get update && apt-get install -y ufw
# - ufw allow ssh
# - ufw allow http
# - ufw allow https
# - ufw enable

Note: The cloud-init script above is a starting point. A production deployment would require more robust handling of Magento installation, configuration, SSL setup, and potentially integration with a CDN and load balancer. The database and cache connection details need to be correctly passed and used. The private_connection_uri for the database is a placeholder; you’ll need to obtain the actual connection string or host/port/credentials from OVH’s managed service interface or API.

Load Balancer and DNS

For high availability and scalability, a load balancer is essential. OVH offers Load Balancer services. DNS configuration will point your domain to the load balancer’s IP address.

OVH Load Balancer (Conceptual)

Terraform can provision an OVH Load Balancer. This typically involves defining the load balancer resource, backend servers (your web instances), and frontend listeners (ports 80, 443).

# loadbalancer.tf (Conceptual Example)

resource "ovh_cloud_project_loadbalancer" "magento_lb" {
  service_name = var.ovh_service_name
  name         = "magento-lb"
  region       = var.ovh_region
  type         = "public" # or "private"
  # Other LB configurations like SSL certificates, health checks, etc.
}

# Associate web instances with the load balancer
resource "ovh_cloud_project_loadbalancer_backend" "magento_lb_backend" {
  count        = ovh_cloud_project_instance.magento_web.count
  service_name = var.ovh_service_name
  lb_id        = ovh_cloud_project_loadbalancer.magento_lb.id
  instance_id  = ovh_cloud_project_instance.magento_web[count.index].id
  port         = 80 # Traffic will be forwarded to web servers on port 80
  weight       = 100
  ssl_healthcheck = false
  tcp_healthcheck = true
  advanced_healthcheck = false
}

# Frontend listener for HTTP
resource "ovh_cloud_project_loadbalancer_frontend" "magento_lb_frontend_http" {
  service_name = var.ovh_service_name
  lb_id        = ovh_cloud_project_loadbalancer.magento_lb.id
  port         = 80
  protocol     = "http"
  default_backend_id = ovh_cloud_project_loadbalancer_backend.magento_lb_backend[0].id # Point to one of the backends
}

# Frontend listener for HTTPS (requires SSL certificate configuration)
# resource "ovh_cloud_project_loadbalancer_frontend" "magento_lb_frontend_https" {
#   service_name = var.ovh_service_name
#   lb_id        = ovh_cloud_project_loadbalancer.magento_lb.id
#   port         = 443
#   protocol     = "https"
#   ssl_certificate_id = "your-ssl-certificate-id" # Obtain from OVH SSL services
#   default_backend_id = ovh_cloud_project_loadbalancer_backend.magento_lb_backend[0].id
# }

Deployment Workflow

The typical workflow for deploying and managing your Magento 2 cluster with Terraform on OVH:

  • Initialize: Run terraform init to download the OVH provider and any other necessary modules.
  • Plan: Execute terraform plan to review the infrastructure changes Terraform will make. This is a critical step to catch potential errors before applying.
  • Apply: Run terraform apply to provision all the resources defined in your configuration. This includes VPC, subnets, security groups, instances, databases, and load balancers.
  • Magento Setup: After instances are provisioned, the cloud-init script will attempt to install and configure software. However, critical Magento setup commands (like setup:install, setup:upgrade, cache configuration) are often best executed post-provisioning via a CI/CD pipeline (e.g., Jenkins, GitLab CI, GitHub Actions) or a configuration management tool (Ansible, Chef, Puppet). This allows for more control over the Magento application deployment itself.
  • Update: Modify your Terraform files (e.g., change instance size, add more web servers) and run terraform plan and terraform apply again to update the infrastructure.
  • Destroy: When no longer needed, run terraform destroy to tear down all provisioned resources and avoid ongoing costs.

Security Best Practices and Considerations

  • Least Privilege: Ensure your OVH API credentials used by Terraform have only the necessary permissions.
  • SSH Key Management: Securely manage your SSH keys. Do not embed private keys in your Terraform code.
  • Database Security: Use private network access for your managed database. Regularly rotate database credentials.
  • Firewall Rules: Strictly define security group rules. Only open necessary ports and restrict source IP addresses where possible.
  • SSL/TLS: Implement SSL/TLS for all HTTPS traffic. Use OVH’s managed SSL certificates or integrate with Let’s Encrypt.
  • Regular Updates: Keep your OS, PHP, Nginx, and Magento versions up-to-date with security patches. Automate this process using configuration management tools.
  • Monitoring and Logging: Set up robust monitoring and logging for your instances and services.
  • Backups: Ensure regular, tested backups of your Magento database and file system.

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