Why Terraform for Odoo?
Terraform lets you define cloud infrastructure in code — servers, databases, networks, firewalls, DNS records, and SSL certificates. Instead of clicking through cloud provider dashboards, you write configuration files that describe your desired infrastructure, and Terraform creates it. Change the config, run terraform apply, and Terraform updates only what changed.
For Odoo deployments, this means you can provision an entire production environment — server, PostgreSQL database, firewall rules, domain configuration, and SSL — with a single command. Need another environment for staging? Copy the config and apply. Need to rebuild after a disaster? Apply the same config to a new region.
Benefits
- Reproducibility: Same infrastructure every time, no manual steps to forget
- Version control: Track infrastructure changes in Git alongside your code
- Documentation: The Terraform config IS the documentation of your infrastructure
- Multi-cloud: Use the same workflow for AWS, GCP, Hetzner, Vultr, etc.
- Team collaboration: Review infrastructure changes in pull requests
- Disaster recovery: Rebuild entire environments from code
Step 1: Install Terraform
# macOS
brew install terraform
# Linux
wget https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_linux_amd64.zip
unzip terraform_*.zip && sudo mv terraform /usr/local/bin/
terraform --versionStep 2: Define Odoo Infrastructure
Example using Hetzner Cloud (affordable for Odoo):
# main.tf
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.45"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
provider "hcloud" {
token = var.hcloud_token
}
provider "cloudflare" {
api_token = var.cloudflare_token
}
# Variables
variable "hcloud_token" { sensitive = true }
variable "cloudflare_token" { sensitive = true }
variable "domain" { default = "erp.yourdomain.com" }
variable "ssh_key_name" { default = "deploy-key" }# server.tf
resource "hcloud_ssh_key" "deploy" {
name = var.ssh_key_name
public_key = file("~/.ssh/id_ed25519.pub")
}
resource "hcloud_server" "odoo" {
name = "odoo-production"
server_type = "cpx31" # 4 vCPU, 8GB RAM
image = "ubuntu-24.04"
location = "nbg1" # Nuremberg
ssh_keys = [hcloud_ssh_key.deploy.id]
labels = {
app = "odoo"
env = "production"
}
user_data = file("cloud-init.yml")
}
resource "hcloud_volume" "odoo_data" {
name = "odoo-data"
size = 100 # GB
location = "nbg1"
format = "ext4"
}
resource "hcloud_volume_attachment" "odoo_data" {
volume_id = hcloud_volume.odoo_data.id
server_id = hcloud_server.odoo.id
}# network.tf
resource "hcloud_firewall" "odoo" {
name = "odoo-firewall"
rule {
direction = "in"
protocol = "tcp"
port = "22"
source_ips = ["0.0.0.0/0", "::/0"]
}
rule {
direction = "in"
protocol = "tcp"
port = "80"
source_ips = ["0.0.0.0/0", "::/0"]
}
rule {
direction = "in"
protocol = "tcp"
port = "443"
source_ips = ["0.0.0.0/0", "::/0"]
}
}
resource "hcloud_firewall_attachment" "odoo" {
firewall_id = hcloud_firewall.odoo.id
server_ids = [hcloud_server.odoo.id]
}# dns.tf
resource "cloudflare_record" "odoo" {
zone_id = var.cloudflare_zone_id
name = "erp"
content = hcloud_server.odoo.ipv4_address
type = "A"
proxied = false
}
output "server_ip" {
value = hcloud_server.odoo.ipv4_address
}
output "odoo_url" {
value = "https://${var.domain}"
}Step 3: Cloud-Init for Auto-Configuration
# cloud-init.yml
#cloud-config
package_update: true
packages:
- postgresql
- nginx
- certbot
- python3-certbot-nginx
- python3-pip
- python3-dev
- python3-venv
- git
- libpq-dev
- libxml2-dev
- libxslt1-dev
runcmd:
- sudo -u postgres createuser --createdb odoo
- adduser --system --home=/opt/odoo --group odoo
- su - odoo -s /bin/bash -c 'git clone https://github.com/odoo/odoo.git --depth 1 --branch 19.0 /opt/odoo/odoo'
- su - odoo -s /bin/bash -c 'python3 -m venv /opt/odoo/venv && source /opt/odoo/venv/bin/activate && pip install -r /opt/odoo/odoo/requirements.txt'Step 4: Apply Infrastructure
# Initialize
terraform init
# Preview changes
terraform plan
# Apply
terraform apply
# Output: server IP, URL
terraform outputStep 5: Multi-Environment Setup
Use Terraform workspaces or separate variable files for different environments:
# Production
terraform apply -var-file=production.tfvars
# Staging
terraform apply -var-file=staging.tfvars
# staging.tfvars
hcloud_token = "staging-token"
server_type = "cpx21" # Smaller for staging
domain = "staging-erp.yourdomain.com"State Management
Store Terraform state remotely for team collaboration:
terraform {
backend "s3" {
bucket = "your-terraform-state"
key = "odoo/production/terraform.tfstate"
region = "eu-central-1"
}
}Provider Examples
| Provider | Terraform Provider | Min Config |
|---|---|---|
| Hetzner | hetznercloud/hcloud | Token |
| Vultr | vultr/vultr | API key |
| DigitalOcean | digitalocean/digitalocean | Token |
| AWS | hashicorp/aws | Access key + secret |
| GCP | hashicorp/google | Service account JSON |
| Cloudflare | cloudflare/cloudflare | API token |
DeployMonkey + Terraform
DeployMonkey handles infrastructure provisioning internally. If you prefer to manage your own infrastructure with Terraform, you can deploy Odoo on your Terraform-managed servers and use DeployMonkey for application-level management.