• 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 Laravel Clusters on Google Cloud Using Terraform

Infrastructure as Code: Provisioning Secure Laravel Clusters on Google Cloud Using Terraform

Terraform Provider Configuration for Google Cloud

To begin provisioning resources on Google Cloud Platform (GCP) with Terraform, we need to configure the Google Cloud provider. This involves specifying your GCP project ID, region, and potentially authentication credentials. For production environments, it’s highly recommended to use a service account with appropriate IAM roles rather than user credentials.

Create a file named main.tf in your Terraform project directory. This file will house our primary infrastructure definitions.

Here’s a basic provider configuration:

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 4.0"
    }
  }
}

provider "google" {
  project = var.gcp_project_id
  region  = var.gcp_region
}

variable "gcp_project_id" {
  description = "The GCP project ID to deploy resources into."
  type        = string
}

variable "gcp_region" {
  description = "The GCP region to deploy resources into."
  type        = string
  default     = "us-central1"
}

# For service account authentication, uncomment and configure:
# variable "gcp_service_account_key_file" {
#   description = "Path to the GCP service account key file."
#   type        = string
#   sensitive   = true
# }
#
# provider "google" {
#   project     = var.gcp_project_id
#   region      = var.gcp_region
#   credentials = file(var.gcp_service_account_key_file)
# }

Securing the Laravel Application: VPC and Firewall Rules

A robust network infrastructure is paramount for application security. We’ll define a Virtual Private Cloud (VPC) network and specific firewall rules to control ingress and egress traffic. This ensures only necessary ports are open to the internet, and internal communication is restricted.

We’ll create a custom VPC network to isolate our resources. This is generally preferred over using the default VPC for better control.

resource "google_compute_network" "laravel_vpc" {
  name                    = "laravel-vpc"
  auto_create_subnetworks = false # We will define our own subnets
  routing_mode            = "REGIONAL"
}

resource "google_compute_subnetwork" "laravel_subnet" {
  name          = "laravel-subnet"
  ip_cidr_range = "10.0.1.0/24"
  region        = var.gcp_region
  network       = google_compute_network.laravel_vpc.id
}

Next, we’ll implement firewall rules. We’ll allow SSH access for management, HTTP/HTTPS for web traffic, and potentially other ports required by your Laravel application (e.g., database connections if not using Cloud SQL private IP).

# Allow SSH access from anywhere (consider restricting this to specific IPs in production)
resource "google_compute_firewall" "allow_ssh" {
  name    = "allow-ssh"
  network = google_compute_network.laravel_vpc.name
  allow {
    protocol = "tcp"
    ports    = ["22"]
  }
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["laravel-server"] # Apply this tag to our instances
}

# Allow HTTP and HTTPS traffic from anywhere
resource "google_compute_firewall" "allow_http_https" {
  name    = "allow-http-https"
  network = google_compute_network.laravel_vpc.name
  allow {
    protocol = "tcp"
    ports    = ["80", "443"]
  }
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["laravel-server"]
}

# Example: Allow internal communication between Laravel servers on a specific port
# resource "google_compute_firewall" "allow_internal_app_port" {
#   name    = "allow-internal-app-port"
#   network = google_compute_network.laravel_vpc.name
#   allow {
#     protocol = "tcp"
#     ports    = ["8000"] # Example application port
#   }
#   source_ranges = ["10.0.1.0/24"] # Our subnet CIDR
#   target_tags   = ["laravel-server"]
# }

# Deny all other ingress traffic by default (implicit in GCP, but good to be explicit conceptually)
# No explicit resource needed here as GCP denies by default.

Provisioning Compute Instances for Laravel Application Servers

We’ll use Google Compute Engine (GCE) instances to host our Laravel application. For scalability and resilience, we’ll configure a managed instance group (MIG) with an auto-scaling policy and a load balancer.

First, let’s define a startup script that will install necessary software (PHP, web server, Composer, etc.) and deploy our Laravel application. This script will be executed when each instance in the MIG starts.

#!/bin/bash
set -e # Exit immediately if a command exits with a non-zero status.

# Update package lists and install essential packages
apt-get update -y
apt-get install -y \
    apache2 \
    php \
    libapache2-mod-php \
    php-mysql \
    php-mbstring \
    php-xml \
    php-zip \
    php-curl \
    php-gd \
    php-intl \
    composer \
    git \
    unzip

# Configure Apache to serve from a specific directory (e.g., /var/www/html/laravel_app)
# This assumes your Laravel app will be deployed to this path.
# You might need to adjust Apache's virtual host configuration for production.
a2enmod rewrite
a2dissite 000-default.conf
# Create a new virtual host for Laravel
cat <<EOF > /etc/apache2/sites-available/laravel.conf
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/laravel_app/public

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <Directory /var/www/html/laravel_app/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
EOF
a2ensite laravel.conf
systemctl reload apache2

# Deploy Laravel Application (Replace with your actual deployment logic)
# This is a simplified example. In production, you'd likely use Git, CI/CD, or a deployment tool.
# Ensure your application is configured to pull from a Git repository or artifact.
APP_DIR="/var/www/html/laravel_app"
mkdir -p $APP_DIR
cd $APP_DIR

# Example: Clone from a private Git repository (ensure SSH keys are managed securely)
# git clone [email protected]:your-org/your-laravel-repo.git .
# Or pull from a public repo for testing:
git clone https://github.com/laravel/laravel.git .
rm -rf .git # Remove git history if not needed for the deployed app

# Install Composer dependencies
composer install --no-dev --optimize-autoloader

# Set up environment variables (ensure .env file is managed securely)
# For production, use GCP Secret Manager or similar.
# cp .env.example .env
# echo "APP_KEY=$(php artisan key:generate --show)" >> .env
# echo "DB_HOST=your_db_host" >> .env
# echo "DB_DATABASE=your_db_name" >> .env
# echo "DB_USERNAME=your_db_user" >> .env
# echo "DB_PASSWORD=your_db_password" >> .env

# Run Laravel migrations (if applicable and database is accessible)
# php artisan migrate --force

# Set correct file permissions
chown -R www-data:www-data $APP_DIR
chmod -R 755 $APP_DIR/storage
chmod -R 755 $APP_DIR/bootstrap/cache

# Restart Apache
systemctl restart apache2

echo "Laravel application deployed and configured."

Now, let’s define the instance template and the managed instance group.

# Instance Template for Laravel application servers
resource "google_compute_instance_template" "laravel_app_template" {
  name_prefix  = "laravel-app-template-"
  machine_type = "e2-medium" # Choose an appropriate machine type
  tags         = ["laravel-server", "http-server", "https-server"]

  disk {
    source_image = "ubuntu-os-cloud/ubuntu-2004-lts" # Or your preferred OS image
    auto_delete  = true
    boot         = true
  }

  network_interface {
    subnetwork = google_compute_subnetwork.laravel_subnet.id
    # Access config for public IP (for initial setup/testing, consider private IP + NAT for production)
    access_config {
      // Ephemeral IP
    }
  }

  metadata_startup_script = file("startup-script.sh") # Path to your startup script

  service_account {
    scopes = ["cloud-platform"] # Grant broad access for simplicity; refine for production
  }

  lifecycle {
    create_before_destroy = true
  }
}

# Managed Instance Group (MIG) for auto-scaling
resource "google_compute_instance_group_manager" "laravel_mig" {
  name               = "laravel-app-mig"
  base_instance_name = "laravel-app"
  zone               = "${var.gcp_region}-a" # Specify a zone within your region
  target_size        = 2 # Initial number of instances

  version {
    instance_template = google_compute_instance_template.laravel_app_template.id
    name              = "v1"
  }

  # Auto-scaling configuration
  auto_healing_policies {
    health_check = google_compute_health_check.laravel_app_health_check.id
    initial_delay_sec = 300 # Wait 5 minutes before starting health checks
  }

  update_policy {
    type = "PROACTIVE"
    minimal_action = "REPLACE"
  }

  # Optional: Rolling updates configuration
  # rolling_update_policy {
  #   max_unavailable_fixed = 1
  #   max_surge_fixed       = 1
  # }
}

# Health Check for Auto-healing
resource "google_compute_health_check" "laravel_app_health_check" {
  name                = "laravel-app-health-check"
  check_interval_sec  = 5
  timeout_sec         = 5
  healthy_threshold   = 2
  unhealthy_threshold = 2

  http_health_check {
    port         = 80
    request_path = "/" # Or a specific health check endpoint in your Laravel app
  }
}

# Auto-scaler configuration
resource "google_compute_autoscaler" "laravel_autoscaler" {
  name   = "laravel-app-autoscaler"
  zone   = google_compute_instance_group_manager.laravel_mig.zone
  target = google_compute_instance_group_manager.laravel_mig.id

  autoscaling_policy {
    max_replicas = 10
    min_replicas = 2
    cooldown_period = 60

    cpu_utilization {
      target = 0.6 # Scale up when CPU utilization reaches 60%
    }

    # You can add other scaling metrics like load balancing serving capacity
    # load_balancing_utilization {
    #   target = 0.8
    # }
  }
}

Load Balancing for High Availability and SSL Termination

A Global External HTTP(S) Load Balancer is crucial for distributing traffic across your MIG instances, providing high availability and enabling SSL termination. This offloads SSL processing from your application servers.

# Backend Service for the Load Balancer
resource "google_compute_backend_service" "laravel_backend" {
  name                  = "laravel-backend-service"
  protocol              = "HTTP"
  port_name             = "http"
  timeout_sec           = 10
  enable_cdn            = false
  load_balancing_scheme = "EXTERNAL_MANAGED"
  backend {
    group = google_compute_instance_group_manager.laravel_mig.instance_group
  }

  health_checks = [google_compute_health_check.laravel_app_health_check.id]
}

# URL Map to route requests to the backend service
resource "google_compute_url_map" "laravel_url_map" {
  name            = "laravel-url-map"
  default_service = google_compute_backend_service.laravel_backend.id
}

# SSL Certificate (Replace with your actual certificate details or use Google-managed certificates)
# For Google-managed certificates, you'll need to create a google_compute_managed_ssl_certificate resource
# and associate it with the target_https_proxy.
# Example for a self-managed certificate:
# resource "google_compute_ssl_certificate" "laravel_ssl_cert" {
#   name        = "laravel-ssl-cert"
#   private_key = file("path/to/your/private.key")
#   certificate = file("path/to/your/certificate.crt")
# }

# Target HTTPS Proxy
resource "google_compute_target_https_proxy" "laravel_https_proxy" {
  name             = "laravel-https-proxy"
  url_map          = google_compute_url_map.laravel_url_map.id
  # ssl_certificates = [google_compute_ssl_certificate.laravel_ssl_cert.id] # Uncomment for self-managed
  # For Google-managed certificates:
  # ssl_certificates = [google_compute_managed_ssl_certificate.laravel_managed_cert.id]
}

# Global Forwarding Rule for HTTPS traffic
resource "google_compute_global_forwarding_rule" "laravel_https_forwarding_rule" {
  name                  = "laravel-https-forwarding-rule"
  ip_protocol           = "TCP"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  port_range            = "443"
  target                = google_compute_target_https_proxy.laravel_https_proxy.id
  ip_address            = google_compute_global_address.laravel_static_ip.name # Use a static IP
}

# Target HTTP Proxy (for redirecting HTTP to HTTPS)
resource "google_compute_target_http_proxy" "laravel_http_proxy" {
  name    = "laravel-http-proxy"
  url_map = google_compute_url_map.laravel_url_map.id
}

# Global Forwarding Rule for HTTP traffic (redirect to HTTPS)
resource "google_compute_global_forwarding_rule" "laravel_http_forwarding_rule" {
  name                  = "laravel-http-forwarding-rule"
  ip_protocol           = "TCP"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  port_range            = "80"
  target                = google_compute_target_http_proxy.laravel_http_proxy.id
  ip_address            = google_compute_global_address.laravel_static_ip.name # Use the same static IP
}

# Reserve a static global IP address
resource "google_compute_global_address" "laravel_static_ip" {
  name = "laravel-static-ip"
}

# If using Google-managed SSL certificates:
# resource "google_compute_managed_ssl_certificate" "laravel_managed_cert" {
#   name = "laravel-managed-ssl-cert"
#   managed {
#     domains = ["your-domain.com"] # Replace with your domain
#   }
# }

Database Provisioning with Cloud SQL

For a production Laravel application, a managed database service like Cloud SQL is highly recommended. We’ll provision a PostgreSQL instance and configure it for secure access.

resource "google_sql_database_instance" "laravel_db" {
  name             = "laravel-db-instance"
  region           = var.gcp_region
  database_version = "POSTGRES_14" # Or your preferred PostgreSQL version
  project          = var.gcp_project_id

  settings {
    tier = "db-f1-micro" # Choose an appropriate tier for your needs
    ip_configuration {
      ipv4_enabled    = true # Enable public IP for initial setup/testing
      private_network = google_compute_network.laravel_vpc.id # Connect to your VPC
      # For enhanced security, disable public IP and use private IP only.
      # You would then need to configure VPC peering or Cloud SQL Auth Proxy for access.
    }
    backup_configuration {
      enabled = true
      binary_log_enabled = false # Not applicable for PostgreSQL
    }
    # Uncomment and configure for High Availability
    # availability_type = "REGIONAL"
  }

  # Prevent accidental deletion of the database instance
  lifecycle {
    prevent_destroy = true
  }
}

resource "google_sql_database" "laravel_app_db" {
  name     = "laravel_app_db"
  instance = google_sql_database_instance.laravel_db.name
  project  = var.gcp_project_id
}

resource "google_sql_user" "laravel_db_user" {
  name     = "laravel_user"
  instance = google_sql_database_instance.laravel_db.name
  host     = "%" # Allow connection from any host (restrict in production)
  password = random_password.db_password.result
  project  = var.gcp_project_id
}

resource "random_password" "db_password" {
  length           = 16
  special          = true
  override_special = "_%@"
}

# Output the database password securely
output "db_password" {
  description = "The password for the Laravel database user."
  value       = random_password.db_password.result
  sensitive   = true
}

Securing Database Access

Directly exposing the database to the internet is a significant security risk. For production, it’s best practice to disable public IP access on the Cloud SQL instance and use the Cloud SQL Auth Proxy or VPC peering for secure connections from your application instances.

If you disable ipv4_enabled on the Cloud SQL instance, you’ll need to ensure your application instances can reach the database via its private IP. This typically involves running the Cloud SQL Auth Proxy as a sidecar container or directly configuring your application to use the private IP and ensuring network connectivity.

# Example of configuring Cloud SQL Auth Proxy in your application's startup script or Dockerfile:
#
# 1. Download the Cloud SQL Auth Proxy binary.
# 2. Run the proxy as a background service, connecting to your database instance.
#    ./cloud_sql_proxy -instances=your-gcp-project:your-region:laravel-db-instance=tcp:5432
# 3. Configure your Laravel application's database connection to use '127.0.0.1' and port '5432' (or the port the proxy is listening on).
#
# Alternatively, if your application instances are within the same VPC as the Cloud SQL private IP,
# you might be able to connect directly using the private IP address.
#
# To restrict database user access:
# resource "google_sql_user" "laravel_db_user" {
#   name     = "laravel_user"
#   instance = google_sql_database_instance.laravel_db.name
#   host     = "your-app-instance-private-ip" # Or a subnet range
#   password = random_password.db_password.result
#   project  = var.gcp_project_id
# }

Deployment Workflow and Considerations

With the Terraform code defined, the deployment workflow is as follows:

  • Initialize Terraform: terraform init
  • Review the execution plan: terraform plan -var="gcp_project_id=your-gcp-project-id"
  • Apply the infrastructure: terraform apply -var="gcp_project_id=your-gcp-project-id"

Key Considerations for Production:

  • Secrets Management: Use GCP Secret Manager for database credentials, API keys, and other sensitive information. Avoid hardcoding secrets in Terraform or startup scripts.
  • CI/CD Integration: Integrate Terraform into your CI/CD pipeline (e.g., GitLab CI, GitHub Actions, Cloud Build) for automated, repeatable deployments.
  • Monitoring and Logging: Implement robust monitoring (Cloud Monitoring) and logging (Cloud Logging) for your application and infrastructure.
  • IAM Roles: Grant the least privilege necessary to service accounts used by Terraform and your application instances.
  • Database Backups and Recovery: Ensure your Cloud SQL backup configuration is robust and test your recovery procedures.
  • SSL Certificates: For production, use Google-managed SSL certificates for automatic renewal and simplified management.
  • Network Security: Further restrict firewall rules to only allow necessary traffic. Consider using Private Google Access for services that don’t require public IPs.
  • Application Deployment Strategy: The startup script is a basic example. For zero-downtime deployments, consider using tools like Ansible, Docker, or Kubernetes, and implement blue/green or canary deployment strategies.

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