Infrastructure as Code: Provisioning Secure Shopify Clusters on DigitalOcean Using Terraform
Terraform Project Structure and Provider Configuration
We’ll begin by establishing a robust Terraform project structure. This ensures maintainability and scalability for our DigitalOcean infrastructure. The core of our setup involves configuring the DigitalOcean provider, specifying API tokens, and defining region preferences. For security, it’s paramount to manage sensitive credentials like API tokens using environment variables or a dedicated secrets management system, rather than hardcoding them directly into your Terraform configuration files.
Create a directory for your Terraform project, for instance, shopify-cluster-tf. Inside this directory, create the following files:
main.tf: The primary configuration file.variables.tf: Defines input variables for customization.outputs.tf: Specifies output values from the deployment.providers.tf: Configures the DigitalOcean provider.
providers.tf
This file declares the DigitalOcean provider and how Terraform will authenticate. Ensure the DIGITALOCEAN_TOKEN environment variable is set before running Terraform commands.
# providers.tf
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
variable "do_token" {
description = "DigitalOcean API Token"
type = string
sensitive = true
}
variable "do_region" {
description = "DigitalOcean region for resources"
type = string
default = "nyc3"
}
output "do_region" {
description = "The DigitalOcean region used for deployment."
value = var.do_region
}
Defining VPC and Security Groups for Network Isolation
A Virtual Private Cloud (VPC) is essential for segmenting your network and controlling traffic flow. We’ll create a dedicated VPC for our Shopify cluster. Alongside the VPC, we’ll define security groups to act as virtual firewalls, allowing only necessary inbound and outbound traffic. This is a critical step in securing your Shopify deployment.
main.tf – VPC and Security Group Resources
# main.tf
resource "digitalocean_vpc" "shopify_vpc" {
name = "shopify-cluster-vpc"
region = var.do_region
ip_range = "10.10.0.0/16" # Example private IP range
}
resource "digitalocean_droplet_firewall" "shopify_firewall" {
name = "shopify-cluster-firewall"
# Allow SSH access from anywhere (consider restricting this in production)
inbound_rule {
protocol = "tcp"
ports = "22"
sources {
addresses = ["0.0.0.0/0"]
}
}
# Allow HTTP and HTTPS for Shopify storefront traffic
inbound_rule {
protocol = "tcp"
ports = "80"
sources {
addresses = ["0.0.0.0/0"]
}
}
inbound_rule {
protocol = "tcp"
ports = "443"
sources {
addresses = ["0.0.0.0/0"]
}
}
# Allow internal communication within the VPC (e.g., between web servers and database)
inbound_rule {
protocol = "tcp"
ports = "0-65535" # Allow all TCP ports for internal communication
sources {
droplet_ids = [digitalocean_droplet.web_server.id, digitalocean_droplet.db_server.id] # Example, adjust as needed
}
}
inbound_rule {
protocol = "udp"
ports = "0-65535" # Allow all UDP ports for internal communication
sources {
droplet_ids = [digitalocean_droplet.web_server.id, digitalocean_droplet.db_server.id] # Example, adjust as needed
}
}
# Allow all outbound traffic (consider restricting this for enhanced security)
outbound_rule {
protocol = "tcp"
ports = "0-65535"
destinations {
addresses = ["0.0.0.0/0"]
}
}
outbound_rule {
protocol = "udp"
ports = "0-65535"
destinations {
addresses = ["0.0.0.0/0"]
}
}
outbound_rule {
protocol = "icmp"
destinations {
addresses = ["0.0.0.0/0"]
}
}
# Associate firewall with the VPC
vpc_ids = [digitalocean_vpc.shopify_vpc.id]
}
# Placeholder for Droplet resources, will be defined in the next section
resource "digitalocean_droplet" "web_server" {}
resource "digitalocean_droplet" "db_server" {}
Note: In a production environment, you should significantly restrict SSH access to specific IP ranges or bastion hosts. Similarly, outbound rules should be as granular as possible.
Provisioning Web Servers and Database Instances
Now, let’s define the Droplets that will host our Shopify application and its database. We’ll configure them with appropriate SSH keys for secure access, assign them to our newly created VPC, and attach the security firewall. For a production Shopify setup, you’d typically have multiple web servers for high availability and a managed database service or a dedicated, highly available database Droplet.
main.tf – Droplet Resources
# main.tf (continued)
variable "ssh_key_fingerprint" {
description = "Fingerprint of the SSH key to be added to Droplets"
type = string
sensitive = true
}
variable "droplet_image" {
description = "The base image for the Droplets (e.g., Ubuntu 22.04)"
type = string
default = "ubuntu-22-04-x64"
}
variable "droplet_size" {
description = "The size slug for the Droplets (e.g., s-2vcpu-4gb)"
type = string
default = "s-2vcpu-4gb"
}
resource "digitalocean_ssh_key" "deploy_key" {
name = "shopify-deploy-key"
public_key = file("~/.ssh/id_rsa.pub") # Ensure this path is correct or use a variable
}
resource "digitalocean_droplet" "web_server" {
name = "shopify-web-01"
region = var.do_region
size = var.droplet_size
image = var.droplet_image
vpc_uuid = digitalocean_vpc.shopify_vpc.id
ssh_keys = [digitalocean_ssh_key.deploy_key.id]
monitoring = true
tags = ["shopify", "web", "production"]
connection {
type = "ssh"
user = "root" # Or your preferred user
private_key = file("~/.ssh/id_rsa") # Ensure this path is correct or use a variable
host = self.ipv4_address
timeout = "2m"
}
provisioner "remote-exec" {
inline = [
"apt-get update -y",
"apt-get install -y nginx", # Example: Install Nginx
"systemctl enable nginx",
"systemctl start nginx",
# Add commands here to configure your Shopify application, e.g.,
# cloning from Git, setting up environment variables, installing dependencies.
# Example:
# "apt-get install -y git",
# "git clone https://github.com/your-shopify-app/your-app.git /var/www/html",
# "cd /var/www/html && composer install --no-dev",
# "cp .env.example .env",
# "sed -i 's/DB_HOST=.*/DB_HOST=your_db_host/' .env", # Example env var update
# "php artisan key:generate",
# "php artisan migrate --force"
]
}
}
resource "digitalocean_droplet" "db_server" {
name = "shopify-db-01"
region = var.do_region
size = "s-4vcpu-8gb" # Larger size for database
image = var.droplet_image
vpc_uuid = digitalocean_vpc.shopify_vpc.id
ssh_keys = [digitalocean_ssh_key.deploy_key.id]
monitoring = true
tags = ["shopify", "database", "production"]
connection {
type = "ssh"
user = "root" # Or your preferred user
private_key = file("~/.ssh/id_rsa") # Ensure this path is correct or use a variable
host = self.ipv4_address
timeout = "2m"
}
provisioner "remote-exec" {
inline = [
"apt-get update -y",
"apt-get install -y mysql-server", # Example: Install MySQL
"systemctl enable mysql",
"systemctl start mysql",
# Add commands here to secure your MySQL installation and configure it for Shopify.
# This might include creating users, databases, and setting up replication.
# Example:
# "mysql -e \"CREATE DATABASE shopify_db;\"",
# "mysql -e \"CREATE USER 'shopify_user'@'%' IDENTIFIED BY 'your_secure_password';\"",
# "mysql -e \"GRANT ALL PRIVILEGES ON shopify_db.* TO 'shopify_user'@'%';\"",
# "mysql -e \"FLUSH PRIVILEGES;\"",
# "sed -i 's/bind-address.*/bind-address = 0.0.0.0/' /etc/mysql/mysql.conf.d/mysqld.cnf", # Allow connections from VPC
# "systemctl restart mysql"
]
}
}
Important Considerations:
- SSH Keys: Ensure your
~/.ssh/id_rsa(private key) and~/.ssh/id_rsa.pub(public key) exist and are correctly referenced. For production, use dedicated SSH keys and manage them securely. - Database Security: The database provisioning commands are illustrative. For a production Shopify store, you must implement robust database security measures, including strong passwords, restricted user privileges, and potentially encryption at rest. Consider using DigitalOcean’s Managed Databases for a more secure and managed solution.
- Application Deployment: The
remote-execprovisioner is suitable for initial setup. For continuous deployment, integrate with CI/CD pipelines that handle application code updates. - High Availability: For a production Shopify cluster, you’ll need multiple web servers behind a load balancer and a highly available database setup (e.g., replication, managed database).
Configuring Load Balancing for High Availability
To ensure your Shopify store remains accessible even if a web server fails, a load balancer is crucial. DigitalOcean Load Balancers distribute incoming traffic across multiple Droplets. We’ll configure a load balancer to direct traffic to our web servers.
main.tf – Load Balancer Resource
# main.tf (continued)
resource "digitalocean_loadbalancer" "shopify_lb" {
name = "shopify-lb"
region = var.do_region
vpc_uuid = digitalocean_vpc.shopify_vpc.id
# Health check to ensure traffic is only sent to healthy Droplets
healthcheck {
port = 80
protocol = "http"
path = "/" # Or a specific health check endpoint for your app
}
# Forwarding rules for HTTP and HTTPS
forwarding_rule {
entry_protocol = "http"
entry_port = 80
target_protocol = "http"
target_port = 80
target_droplet_ids = [digitalocean_droplet.web_server.id] # Add more web servers here
}
forwarding_rule {
entry_protocol = "https"
entry_port = 443
target_protocol = "https"
target_port = 443
target_droplet_ids = [digitalocean_droplet.web_server.id] # Add more web servers here
}
# Enable sticky sessions if your application requires it (e.g., for session state)
# sticky_sessions {
# type = "cookies"
# }
tags = ["shopify", "loadbalancer", "production"]
}
# Update the web server resource to include the load balancer's IP in its configuration
# This is a simplified example; in a real scenario, you'd configure your web server
# to be aware of the load balancer and potentially handle SSL termination.
resource "digitalocean_droplet" "web_server" {
# ... (previous configuration) ...
provisioner "remote-exec" {
inline = [
"apt-get update -y",
"apt-get install -y nginx",
"systemctl enable nginx",
"systemctl start nginx",
# Example: Configure Nginx to proxy pass to the load balancer (if SSL termination is on LB)
# Or configure it to listen on the VPC IP and let the LB handle external traffic.
# For SSL termination on the LB, your web servers might not need direct public IPs.
# If SSL is terminated on the web servers, you'd need to configure certificates.
# Example for proxying to LB (if LB handles SSL):
# "echo 'server { listen 80; server_name your-domain.com; location / { proxy_pass http://<LOAD_BALANCER_IP>; 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; } }' > /etc/nginx/sites-available/shopify",
# "ln -sf /etc/nginx/sites-available/shopify /etc/nginx/sites-enabled/",
# "nginx -s reload"
]
}
}
Note: The target_droplet_ids in the forwarding rules should dynamically include all your web server Droplets. If you have multiple web servers, you’ll need to manage this list. For SSL termination, you can either configure it on the load balancer itself (recommended for simplicity) or on each web server.
Outputting Key Information
Finally, define outputs to easily retrieve important information about your deployed infrastructure, such as the load balancer’s IP address and the VPC ID. This is invaluable for post-deployment management and integration with other systems.
outputs.tf
# outputs.tf
output "shopify_vpc_id" {
description = "The ID of the created VPC for the Shopify cluster."
value = digitalocean_vpc.shopify_vpc.id
}
output "shopify_loadbalancer_ip" {
description = "The public IP address of the Shopify load balancer."
value = digitalocean_loadbalancer.shopify_lb.ip
}
output "shopify_web_server_private_ip" {
description = "The private IP address of the primary Shopify web server."
value = digitalocean_droplet.web_server.ipv4_address_private
}
output "shopify_db_server_private_ip" {
description = "The private IP address of the Shopify database server."
value = digitalocean_droplet.db_server.ipv4_address_private
}
Deployment Workflow
With the Terraform configuration in place, the deployment process is straightforward:
- Initialize Terraform: Navigate to your project directory in the terminal and run:
terraform init
- Set Environment Variables: Ensure your DigitalOcean API token is set.
export DIGITALOCEAN_TOKEN="your_do_api_token"
- Review the Plan: Before applying any changes, review the execution plan to understand what Terraform will create, modify, or destroy.
terraform plan -var="do_token=your_do_api_token" -var="ssh_key_fingerprint=your_ssh_key_fingerprint"
Replace
your_ssh_key_fingerprintwith the actual fingerprint of your SSH key. You can find this by runningssh-keygen -lf ~/.ssh/id_rsa.pub. - Apply the Configuration: If the plan looks correct, apply the changes to provision your infrastructure.
terraform apply -var="do_token=your_do_api_token" -var="ssh_key_fingerprint=your_ssh_key_fingerprint"
- Destroy Resources (When Needed): To tear down the entire infrastructure, use the destroy command.
terraform destroy -var="do_token=your_do_api_token" -var="ssh_key_fingerprint=your_ssh_key_fingerprint"
Advanced Security Considerations and Next Steps
This setup provides a foundational secure infrastructure for your Shopify cluster. For production readiness, consider the following:
- Managed Databases: Utilize DigitalOcean Managed Databases for PostgreSQL or MySQL. This offloads database management, backups, and high availability to DigitalOcean, significantly enhancing security and reliability.
- SSL/TLS Certificates: Implement SSL/TLS certificates for HTTPS. This can be managed on the load balancer or on each web server. Let’s Encrypt is a popular free option.
- Secrets Management: Integrate with a dedicated secrets management solution (e.g., HashiCorp Vault, AWS Secrets Manager, or DigitalOcean Secrets) for API keys, database credentials, and other sensitive information.
- Monitoring and Alerting: Set up comprehensive monitoring for your Droplets, load balancer, and application. Configure alerts for performance degradation, errors, and security events.
- Automated Backups: Implement robust backup strategies for your database and application data.
- CI/CD Integration: Automate your application deployments using CI/CD pipelines (e.g., GitLab CI, GitHub Actions, Jenkins) to ensure consistent and secure deployments.
- Immutable Infrastructure: For enhanced security and predictability, consider adopting an immutable infrastructure approach where servers are replaced rather than updated in place.