Skip to content

Odoo Infrastructure as Code with Terraform (2026)

DeployMonkey Team · March 23, 2026 11 min read

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 --version

Step 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 output

Step 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

ProviderTerraform ProviderMin Config
Hetznerhetznercloud/hcloudToken
Vultrvultr/vultrAPI key
DigitalOceandigitalocean/digitaloceanToken
AWShashicorp/awsAccess key + secret
GCPhashicorp/googleService account JSON
Cloudflarecloudflare/cloudflareAPI 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.