Skip to Content

Odoo on Google Cloud: how to host it properly

Odoo on Google Cloud: how to host it properly
November 3, 2026 · 22 min read

Short answer

To host Odoo on Google Cloud, run it on a Compute Engine VM with a standard x86 machine type, such as e2-standard-2. Put Postgres on the same VM or on Cloud SQL with a private IP. Open only ports 80 and 443, and log in through IAP instead of public SSH. Send backups of the database and the files to a Cloud Storage bucket that the VM can write to but not read or delete. Skip shared-core machine types for live use, and test a restore before you go live.

Many companies already have a Google Cloud project, so Odoo goes there too. Google Cloud has regions on every continent. It also has many ways to get Odoo slightly wrong. This guide gives you the exact commands for each Google Cloud step.

For the install itself (Odoo, Postgres, the config file and the proxy), follow our guide on running Odoo on a VPS. This page covers what's different on Google Cloud.

You can run every gcloud command below from your own computer with the Google Cloud CLI, or from Cloud Shell in the console. Replace the names in capitals with your own. Most commands need a zone or a region. Set them once so you can leave them out:

gcloud config set project PROJECT_ID
gcloud config set compute/region europe-west1
gcloud config set compute/zone europe-west1-b

Which machine type to pick

Minimum to start: 2 vCPU, 4 GB of memory and 50 GB of disk. That runs Odoo and Postgres on one VM for a small team. Memory matters more than CPU: each worker needs its share, and Postgres wants its own. For more users, big imports or Cloud SQL, our guide on how many Odoo workers you need shows how to size it.

Why not a shared-core type

The e2-micro, e2-small and e2-medium types are "shared-core." They show 2 vCPUs to the system, but you only get part of them all the time:

  • e2-micro: a quarter of a vCPU
  • e2-small: half a vCPU
  • e2-medium: one vCPU

They can use the full 2 vCPUs in short bursts, from 30 seconds to 2 minutes, and only now and then. Odoo is busy all day while people work in it. So when the burst ends, Odoo slows down.

Shared-core types are fine for a test copy that few people use.

Standard types for production

Pick a standard type with full vCPUs. Some examples, all with 2 vCPUs and 8 GB of memory:

  • e2-standard-2. A good default for a small team.
  • n2-standard-2. A newer, faster chip.
  • n4-standard-2. The newest of the three. It only takes Hyperdisk (see the disk note below).

If Postgres runs on the same VM and the database is large, a "highmem" type gives more memory per vCPU. For example, n4-highmem-2 has 2 vCPUs and 16 GB.

x86 only, if you may hand over the work later

Google Cloud also sells Arm machine types: Tau T2A, and Axion types such as C4A and N4A. Our bring your own server software runs on x86 servers only. If you may hand the Odoo work to us later, stay on x86 types like the ones above.

Image

Use Ubuntu 22.04 LTS (image family ubuntu-2204-lts, project ubuntu-os-cloud) or Debian 12 (image family debian-12, project debian-cloud). wkhtmltopdf, which Odoo uses to print PDFs, has official packages for both: Ubuntu 22.04 (jammy) and Debian 12 (bookworm). Print an invoice before you go live.

Disk

Keep everything on the boot disk, or on another persistent disk. Don't use Local SSD for data: it is a temporary disk.

The disk type depends on the machine type:

  • E2 and N2: use pd-balanced.
  • N4: use hyperdisk-balanced. N4 does not take Persistent Disk at all.

Leave room for the filestore and three days of local backups. You can make a disk bigger later, but never smaller.

Region

Pick the region with care. Your data lives in the region you choose. Put the VM and Cloud SQL in the same region. The backup bucket goes in a second region on purpose, as shown below.

Where Postgres goes

You have two good options:

  • On the same VM. Simple, and fine for many teams. Follow the VPS guide as written.
  • On Cloud SQL for PostgreSQL. Google handles the database server, patches and point-in-time recovery. The steps are below.

Give Cloud SQL a private address

Cloud SQL should have a private IP only, inside your VPC network. For that, the network needs "private services access." This guide uses the default network. Run these once:

gcloud services enable servicenetworking.googleapis.com sqladmin.googleapis.com compute.googleapis.com

gcloud compute addresses create google-managed-services-default \
  --global \
  --purpose=VPC_PEERING \
  --prefix-length=16 \
  --network=default

gcloud services vpc-peerings connect \
  --service=servicenetworking.googleapis.com \
  --ranges=google-managed-services-default \
  --network=default

The first command turns on the APIs. The second sets aside a block of private addresses for Google's services. The third connects that block to your network. You need the Compute Network Admin role for this.

Create the database

gcloud sql instances create odoo-db \
  --database-version=POSTGRES_16 \
  --edition=enterprise \
  --cpu=2 --memory=8GB \
  --region=europe-west1 \
  --network=default \
  --no-assign-ip \
  --storage-type=SSD \
  --storage-size=50 \
  --backup-start-time=01:00 \
  --retained-backups-count=14 \
  --enable-point-in-time-recovery \
  --retained-transaction-log-days=7 \
  --ssl-mode=ENCRYPTED_ONLY \
  --deletion-protection

What these do:

  • --edition=enterprise picks the Enterprise edition. For Postgres 16 and later, Cloud SQL picks Enterprise Plus unless you say otherwise. Enterprise Plus uses other machine types and does not take --cpu and --memory.
  • --network=default --no-assign-ip gives the database a private IP and no public one.
  • --backup-start-time=01:00 takes a backup every night at 01:00 UTC.
  • --retained-backups-count=14 keeps 14 of them. The default for Enterprise is 7.
  • --enable-point-in-time-recovery lets you restore to any second in the last 7 days. 7 days is the most Enterprise keeps.
  • --ssl-mode=ENCRYPTED_ONLY refuses connections without SSL. The default allows both.
  • --deletion-protection stops anyone from deleting the database by mistake.
  • Add --availability-type=regional if you want a standby copy in a second zone.

Storage grows by itself when it gets full. It never shrinks.

Find the database's private address:

gcloud sql instances describe odoo-db --format="value(ipAddresses[0].ipAddress)"

We call it CLOUD_SQL_IP below.

Create the odoo database user

Odoo needs its own user that can create databases. Don't use the postgres admin user for daily work.

Cloud SQL makes the postgres user without a password. Set one:

gcloud sql users set-password postgres --instance=odoo-db --prompt-for-password

On the Odoo VM, connect with psql and create the user:

psql "host=CLOUD_SQL_IP user=postgres dbname=postgres sslmode=require"
CREATE ROLE odoo WITH LOGIN CREATEDB PASSWORD 'a-long-random-password';

The postgres user is not a full Postgres superuser, but it can create roles like this one. We create odoo with SQL, not with gcloud sql users create. A user made with gcloud joins the cloudsqlsuperuser group and can also create roles, which Odoo doesn't need.

Check that odoo can log in to the postgres database. Odoo connects there for chat and for scheduled jobs:

psql "host=CLOUD_SQL_IP user=odoo dbname=postgres sslmode=require" -c "SELECT current_user;"

Let Odoo create its database

Create yourdb while connected as odoo, so odoo owns it. Either use the Odoo database manager once, before you set list_db = False, or run this as the odoo user:

odoo -c /etc/odoo/odoo.conf -d yourdb -i base --stop-after-init

Check the owner:

psql "host=CLOUD_SQL_IP user=odoo dbname=postgres sslmode=require" -c "\l yourdb"

If you would rather create it as postgres, Postgres 16 first needs postgres to be a member of odoo:

GRANT odoo TO postgres;
CREATE DATABASE yourdb OWNER odoo ENCODING 'UTF8' LC_COLLATE 'C' TEMPLATE template0;

Point Odoo at Cloud SQL

In odoo.conf:

db_host = CLOUD_SQL_IP
db_port = 5432
db_user = odoo
db_password = a-long-random-password
db_sslmode = require
db_maxconn = 16

db_sslmode = require makes Odoo always use SSL, which the database now demands.

Watch the connection limit

Each Odoo process keeps its own pool of up to db_maxconn connections. The default is 64. Count one pool per worker, one per cron worker (max_cron_threads, 2 by default) and one for the chat process. With 4 workers, that is 7 pools. Cloud SQL sets its own limit from the machine size. Check it:

psql "host=CLOUD_SQL_IP user=odoo dbname=postgres sslmode=require" -c "SHOW max_connections;"

Pools times db_maxconn should stay below that number. With 7 pools and db_maxconn = 16, Odoo can open at most 112.

Match the Postgres client version

pg_dump will not dump a server with a newer major version than itself. If Cloud SQL runs Postgres 16, the Odoo VM needs the version 16 client. Install it from the PostgreSQL apt repository:

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-client-16

Check that both match:

pg_dump --version
psql "host=CLOUD_SQL_IP user=odoo dbname=postgres sslmode=require" -c "SHOW server_version;"

Cloud SQL backups don't cover the filestore

Cloud SQL backs up the database and can restore it to any second in the recovery window. It does not back up the filestore. Attachments and images live on the VM's disk, under /var/lib/odoo/filestore. You still need the Cloud Storage backup below, so the database and the files are saved together.

Network, service account and the VM

A service account for the VM

The VM needs an identity in Google Cloud. It lets it send logs and disk metrics, and later write backups. Every new VM gets the "Compute Engine default service account" unless you pick another. That account may hold the Editor role on your whole project. Make a separate one with only what Odoo needs:

gcloud iam service-accounts create odoo-server --display-name="Odoo server"
SA=odoo-server@PROJECT_ID.iam.gserviceaccount.com

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:$SA" --role=roles/logging.logWriter
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:$SA" --role=roles/monitoring.metricWriter

A VM has two layers of access: "scopes" on the VM and IAM roles on the account. Both must allow a call. Google's advice is to set the full cloud-platform scope and control access with IAM roles only. That's what the VM below does.

Firewall rules

The VM gets a network tag, odoo. Firewall rules point at that tag, not at an IP address.

Open the web ports to everyone:

gcloud compute firewall-rules create odoo-allow-web \
  --network=default --direction=INGRESS --action=allow \
  --rules=tcp:80,tcp:443 --source-ranges=0.0.0.0/0 \
  --target-tags=odoo

Allow SSH only from IAP, Google's login tunnel. IAP always connects from 35.235.240.0/20:

gcloud compute firewall-rules create odoo-allow-iap-ssh \
  --network=default --direction=INGRESS --action=allow \
  --rules=tcp:22 --source-ranges=35.235.240.0/20 \
  --target-tags=odoo

The default network comes with rules that open port 22 and port 3389 to the whole internet, for every VM on it. List them:

gcloud compute firewall-rules list --filter="network=default"

If nothing else in the project needs them, delete them:

gcloud compute firewall-rules delete default-allow-ssh default-allow-rdp

Odoo's own ports (8069 and 8072) stay closed. No rule opens them, so Google Cloud blocks them. Only nginx on the VM talks to them.

A fixed address

Reserve a static external IP, so the address doesn't change when the VM stops and starts:

gcloud compute addresses create odoo-ip --region=europe-west1
gcloud compute addresses describe odoo-ip --region=europe-west1 --format="value(address)"

Point your domain's DNS record at that address before you ask Let's Encrypt for a certificate.

Create the VM

gcloud compute instances create odoo-prod \
  --zone=europe-west1-b \
  --machine-type=e2-standard-2 \
  --image-family=ubuntu-2204-lts --image-project=ubuntu-os-cloud \
  --boot-disk-size=50GB --boot-disk-type=pd-balanced \
  --service-account="$SA" --scopes=cloud-platform \
  --tags=odoo \
  --address=odoo-ip \
  --metadata=enable-oslogin=TRUE

For an N4 type, use --boot-disk-type=hyperdisk-balanced. For Debian, use --image-family=debian-12 --image-project=debian-cloud.

enable-oslogin=TRUE turns on OS Login. You log in with your Google account and its IAM roles, not with SSH keys stored on the VM.

Log in through IAP, not public SSH

Give yourself the roles to log in. Project owners already have them:

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member=user:[email protected] --role=roles/iap.tunnelResourceAccessor
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member=user:[email protected] --role=roles/compute.osAdminLogin
gcloud iam service-accounts add-iam-policy-binding "$SA" \
  --member=user:[email protected] --role=roles/iam.serviceAccountUser
  • iap.tunnelResourceAccessor lets you use the IAP tunnel.
  • compute.osAdminLogin lets you log in with sudo. Use roles/compute.osLogin for people who don't need sudo.
  • iam.serviceAccountUser is needed because the VM runs as the odoo-server account.

Then connect:

gcloud compute ssh odoo-prod --zone=europe-west1-b --tunnel-through-iap

The VM has a public IP, so you must add --tunnel-through-iap. Without it, gcloud tries the public address, and port 22 is closed there.

Check what's open

From your own computer, not from Cloud Shell, check that the private ports are closed. Both commands should time out:

nc -vz -w 5 STATIC_IP 22
curl -m 5 http://STATIC_IP:8069

And that the site answers:

curl -I https://odoo.example.com/web/login

Backups to Cloud Storage

Create the bucket

Put the bucket in a different region from the VM. If the whole region has a problem, your backups are still safe. Keep it in a region where your data is allowed to live.

gcloud storage buckets create gs://your-odoo-backups \
  --location=europe-west4 \
  --uniform-bucket-level-access \
  --public-access-prevention \
  --soft-delete-duration=30d

Bucket names are shared by all Google Cloud customers, so add something unique to yours. You can't move a bucket after you create it.

  • --location=europe-west4 is a second region. You can also use a dual-region, which keeps two copies in two regions. For example, EUR4 is Finland plus the Netherlands, and NAM4 is Iowa plus South Carolina.
  • --uniform-bucket-level-access means only IAM decides who gets in. There are no per-file permissions.
  • --public-access-prevention blocks any public link to the bucket.
  • --soft-delete-duration=30d keeps deleted files for 30 days before they are gone for good. New buckets keep them 7 days. You can restore a soft-deleted file from the console or with gcloud.

Google suggests soft delete instead of object versioning, so this guide uses soft delete. Your backup files never get overwritten anyway, as the next steps show.

Check the settings:

gcloud storage buckets describe gs://your-odoo-backups

Remove old backups on a schedule

Save this as lifecycle.json:

{
  "rule": [
    {
      "action": { "type": "Delete" },
      "condition": { "age": 30 }
    }
  ]
}
gcloud storage buckets update gs://your-odoo-backups --lifecycle-file=lifecycle.json
gcloud storage buckets describe gs://your-odoo-backups --format="default(lifecycle_config)"

Backups older than 30 days are deleted. With soft delete on, "deleted" first means "hidden." The hidden copy stays 30 more days, then goes for good. A new rule can take up to 24 hours to start working.

Let the VM write, and only write

Give the VM's service account one role, on this bucket only:

gcloud storage buckets add-iam-policy-binding gs://your-odoo-backups \
  --member="serviceAccount:$SA" --role=roles/storage.objectCreator

gcloud storage buckets get-iam-policy gs://your-odoo-backups

roles/storage.objectCreator can create files. It can't list, read, overwrite or delete them. If someone breaks into the server, they can't wipe your backups.

One catch. For files over 150 MiB, gcloud splits the upload into parts and joins them at the end. Joining and cleaning up the parts needs read and delete rights, which this role doesn't have. So turn that off for the odoo user, on the VM:

sudo -u odoo -H gcloud config set storage/parallel_composite_upload_enabled False
sudo -u odoo -H gcloud config get storage/parallel_composite_upload_enabled

Big files still upload, just in one stream.

If your rules need more, a retention policy can block deletes for a set time, even by an admin. For example, gcloud storage buckets update gs://your-odoo-backups --retention-period=30d. If you then lock it with --lock-retention-period, you can never remove it or make it shorter. Test it on a spare bucket first.

The gcloud CLI on the VM

Your image may already have the Google Cloud CLI. Check on the VM:

gcloud --version

If it's missing, install it from Google's apt repository:

curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install -y google-cloud-cli

On the VM, gcloud uses the service account by itself. You never store a key on the server.

The backup script

Create the local folder first:

sudo mkdir -p /backups
sudo chown odoo:odoo /backups

Save this as /usr/local/bin/odoo-backup.sh and run sudo chmod 755 on it:

#!/bin/bash
# /usr/local/bin/odoo-backup.sh, run as the odoo user
set -euo pipefail
DB=yourdb
DAY=$(date +%F)
DIR=/backups/$DAY
mkdir -p "$DIR"
pg_dump -Fc "$DB" > "$DIR/$DB.dump"
tar -czf "$DIR/$DB-filestore.tar.gz" -C /var/lib/odoo/filestore "$DB"
gcloud storage cp "$DIR"/* "gs://your-odoo-backups/$DAY/"
find /backups -mindepth 1 -maxdepth 1 -type d -mtime +3 -exec rm -rf {} +

The VM can't overwrite files in the bucket. So if the script runs twice on the same day, the second upload fails. That's on purpose: a backup already in the bucket can't be replaced.

With Cloud SQL, pg_dump must connect over the network. Change the pg_dump line to:

pg_dump -h CLOUD_SQL_IP -U odoo -Fc "$DB" > "$DIR/$DB.dump"

Then give the odoo user its password in a .pgpass file, in its home folder:

sudo -u odoo -H bash -c 'echo "CLOUD_SQL_IP:5432:*:odoo:a-long-random-password" > ~/.pgpass && chmod 600 ~/.pgpass'

The * lets the same line work for every database, including the restore test.

Run it every night

Create /etc/systemd/system/odoo-backup.service:

[Unit]
Description=Odoo backup to Cloud Storage
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=odoo
ExecStart=/usr/local/bin/odoo-backup.sh

And /etc/systemd/system/odoo-backup.timer:

[Unit]
Description=Run the Odoo backup every night

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target

Turn it on, then run it once by hand and check the result:

sudo systemctl daemon-reload
sudo systemctl enable --now odoo-backup.timer
sudo systemctl start odoo-backup.service
journalctl -u odoo-backup.service -n 20

From your own computer, check that the files arrived:

gcloud storage ls --recursive gs://your-odoo-backups/

Test a restore

The production VM can't read the bucket. That's on purpose. So the restore test runs on a second VM with its own read-only service account. Create the account and give it read access to the bucket:

gcloud iam service-accounts create odoo-restore --display-name="Odoo restore test"
RSA=odoo-restore@PROJECT_ID.iam.gserviceaccount.com

gcloud storage buckets add-iam-policy-binding gs://your-odoo-backups \
  --member="serviceAccount:$RSA" --role=roles/storage.objectViewer

roles/storage.objectViewer can list and read files. It can't write or delete them.

Create the test VM with that account, the same tag and no static IP:

gcloud compute instances create odoo-restore-test \
  --zone=europe-west1-b \
  --machine-type=e2-standard-2 \
  --image-family=ubuntu-2204-lts --image-project=ubuntu-os-cloud \
  --boot-disk-size=50GB --boot-disk-type=pd-balanced \
  --service-account="$RSA" --scopes=cloud-platform \
  --tags=odoo \
  --metadata=enable-oslogin=TRUE

To log in, give yourself roles/iam.serviceAccountUser on $RSA too, as you did for $SA. Install Odoo on it with the VPS guide.

On the test VM, as the odoo user, pull one night's backup and restore it:

mkdir -p /tmp/restore && cd /tmp/restore
gcloud storage cp "gs://your-odoo-backups/2026-10-25/*" .
createdb -O odoo yourdb_restored
pg_restore -d yourdb_restored --no-owner --role=odoo yourdb.dump
mkdir -p /var/lib/odoo/filestore/yourdb_restored
tar -xzf yourdb-filestore.tar.gz -C /var/lib/odoo/filestore/yourdb_restored --strip-components=1

If the test copy uses Cloud SQL, add -h CLOUD_SQL_IP -U odoo to createdb and pg_restore.

Before you start Odoo on the copy, neutralize it. A copy of production still has your mail servers and scheduled jobs, so it can email real customers. As the odoo user, run:

odoo neutralize -c /etc/odoo/odoo.conf -d yourdb_restored

Use odoo-bin instead of odoo if you run Odoo from source. This turns off outgoing mail servers, scheduled actions and payment providers in the copy. Our guide on copying production to staging explains what it covers and what it misses.

Then log in to yourdb_restored and open a few attachments. Our guide on how to know if an Odoo backup will restore covers the full test.

Get an alert before the disk fills up

Logs, the filestore and local backups all grow. Google Cloud does not see how full your disk is unless the VM sends that number. The Ops Agent does this.

Install it on the VM:

curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
sudo bash add-google-cloud-ops-agent-repo.sh --also-install
sudo systemctl status google-cloud-ops-agent"*"

The status should show the logging and metrics parts running. The agent uses the logWriter and metricWriter roles you gave the service account.

The disk alert

Create an email channel for alerts, and note its name:

gcloud beta monitoring channels create --display-name="Odoo alerts" \
  --type=email [email protected]
gcloud beta monitoring channels list --format="value(name,displayName)"

The metric is agent.googleapis.com/disk/percent_used. It has one line per disk partition, named by its device label. So find the name of the root partition first. In the console, go to Monitoring > Metrics explorer, pick VM Instance > Disk > Disk utilization, filter on state = used and group by device. On the VM, findmnt -n -o SOURCE / shows the same partition, for example /dev/sda1. Use the name exactly as Metrics explorer shows it.

Find the VM's ID:

gcloud compute instances describe odoo-prod --zone=europe-west1-b --format="value(id)"

Save this as disk-alert.json, with your device name and VM ID:

{
  "displayName": "Odoo disk above 80%",
  "combiner": "OR",
  "conditions": [
    {
      "displayName": "Root disk used above 80%",
      "conditionThreshold": {
        "filter": "metric.type=\"agent.googleapis.com/disk/percent_used\" AND resource.type=\"gce_instance\" AND resource.labels.instance_id=\"INSTANCE_ID\" AND metric.labels.state=\"used\" AND metric.labels.device=\"DEVICE\"",
        "aggregations": [
          { "alignmentPeriod": "300s", "perSeriesAligner": "ALIGN_MEAN" }
        ],
        "comparison": "COMPARISON_GT",
        "thresholdValue": 80,
        "duration": "0s",
        "trigger": { "count": 1 }
      }
    }
  ]
}
gcloud monitoring policies create --policy-from-file=disk-alert.json \
  --notification-channels=CHANNEL_NAME

Filtering on one device keeps out the small read-only disks that Ubuntu adds for snap packages. Those always show 100% full.

An uptime check

Also check that the site itself answers. In the console, go to Monitoring > Uptime checks > Create Uptime Check and set:

  • Protocol: HTTPS
  • Resource Type: URL
  • Hostname: your Odoo domain
  • Path: /web/login

Keep the alert toggle on and pick your Odoo alerts channel. You get an email when at least two checker regions can't reach the page for a minute.

Outgoing email

Compute Engine blocks outgoing connections to port 25 on outside servers. Odoo mail sent that way never leaves. Ports 587 and 465 are open. So send through an SMTP service on one of those. Your current mail provider works. If you use Google Workspace, its SMTP relay works on 587 or 465 only.

In Odoo, turn on developer mode. Go to Settings > Technical > Outgoing Mail Servers. Set your provider's SMTP server, port 587 with encryption TLS (STARTTLS), or port 465 with SSL/TLS, and the SMTP user and password.

Odoo traps on any server, Google Cloud included

Websocket. Since Odoo 16, chat and live updates use port 8072. nginx must send /websocket there, with the upgrade headers.

Proxy mode. Odoo only trusts forwarded headers when proxy_mode = True and the request has X-Forwarded-Host. Without both, your links come out on http://.

Database manager. Set list_db = False and a strong admin_passwd.

Workers. Set workers above 0, sized to memory. Then Odoo starts the chat worker on gevent_port.

In odoo.conf:

proxy_mode = True
list_db = False
admin_passwd = a-long-random-string
workers = 4
gevent_port = 8072

In nginx, next to your location / block:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

location /websocket {
  proxy_pass http://127.0.0.1:8072;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;
  proxy_set_header X-Forwarded-Host $http_host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Real-IP $remote_addr;
}

The map block goes in the http section, outside server.

Behind a Google Cloud load balancer

You don't need a load balancer for one VM. If you use an external Application Load Balancer anyway:

  • It handles websockets with no extra setup. Keep nginx behind it, so /websocket still goes to 8072.
  • It keeps the original Host header and sends X-Forwarded-Proto and X-Forwarded-For, but not X-Forwarded-Host. Odoo ignores the others without it. So nginx must add it.
  • It ends HTTPS, so nginx sees plain HTTP. Pass on the load balancer's protocol, not nginx's own.
  • Its backend service timeout is 30 seconds by default. If long reports or chat connections drop, raise it.

In both nginx location blocks, use:

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;

Then change odoo-allow-web so port 80 is open only to the load balancer, not to the whole internet. The load balancer and its health checks come from 35.191.0.0/16 and 130.211.0.0/22:

gcloud compute firewall-rules update odoo-allow-web \
  --rules=tcp:80 --source-ranges=35.191.0.0/16,130.211.0.0/22

Before you go live

  • The VM is a standard x86 type (not shared-core), with a pd-balanced or hyperdisk-balanced disk
  • The VM runs as its own service account, not the default one
  • Odoo runs with workers above 0, sized to the server's memory
  • /websocket goes to port 8072, and chat works
  • proxy_mode = True, and links in emails start with https://
  • list_db = False and a strong admin_passwd
  • Only ports 80 and 443 are open to the internet, default-allow-ssh is gone, and you log in through IAP
  • With Cloud SQL: private IP only, ENCRYPTED_ONLY, db_sslmode = require, and pg_dump matches the server version
  • The SSL certificate renews on its own
  • PDFs print (wkhtmltopdf works)
  • Test emails arrive, sent on port 587 or 465
  • Logs rotate, the Ops Agent runs, and the disk alert and uptime check point at your email
  • The backup timer ran, and the files are in the bucket, in another region
  • A backup has been restored on another VM, and the attachments open

Keep Google Cloud, hand over the Odoo work

You can keep Odoo in your Google Cloud project and still not run it yourself. With bring your own server, the VM stays in your project and your chosen region. We run Odoo on it: deploys from Git, staging, backups, updates and help from Odoo experts. You keep the VM running and big enough. Our software connects out from your VM, so you don't open any port to us.

Skysize is built by ex-Odoo engineers and is ISO 27001 certified.

Talk to an Odoo hosting expert

FAQ

Can Odoo run on Google Cloud? Yes. Use a Compute Engine VM, Postgres on the VM or on Cloud SQL, and backups to Cloud Storage.

Can I use Cloud SQL for Odoo? Yes. Give it a private IP, turn on SSL, and create a database user for Odoo that can create databases. Cloud SQL doesn't back up the filestore, so back that up too.

Which machine type is best for Odoo? One with enough memory for your workers and Postgres, and full vCPUs, such as e2-standard-2 or n4-standard-2. Avoid shared-core types like e2-medium for production use.

Can Odoo run on Arm machine types like C4A or T2A? They are not covered by this guide. Our bring your own server software runs on x86 only, so pick an x86 type if you may hand the work to us later.

What IAM role does the Odoo VM need for backups? Only roles/storage.objectCreator on your backup bucket. Restores use a separate service account, on another VM, with roles/storage.objectViewer.

Do I need to open port 22? Not to the internet. Allow port 22 only from IAP's range, 35.235.240.0/20, and connect with gcloud compute ssh --tunnel-through-iap.

Why doesn't Odoo send email from Compute Engine? Google Cloud blocks outgoing port 25 to outside servers. Send through your mail provider on port 587 or 465.

Odoo on Google Cloud: how to host it properly
Writes about Odoo infrastructure at Skysize, a Odoo hosting provider based in Belgium, made by ex-Odoo engineers and ISO 27001 certified, serving businesses and agencies worldwide.