Ir al contenido

Odoo en Azure: cómo alojarlo bien

Odoo en Azure: cómo alojarlo bien
noviembre 4, 2026 · 27 min leído

La respuesta corta

Para alojar Odoo en Azure, hazlo correr en una VM x86 de uso general con un disco Premium SSD. Pon Postgres en la misma VM o en Azure Database for PostgreSQL. Abre solo los puertos 80 y 443, y entra a través de Azure Bastion en lugar de un puerto SSH público. Envía las copias de la base de datos y de los archivos a un contenedor de Blob Storage en el que la VM puede escribir, pero no leer ni borrar. No uses VMs de la serie B en producción, y prueba una restauración antes de salir en vivo.

Muchas empresas ya trabajan con Microsoft 365 y Azure, así que Odoo también va allí. Azure tiene una región cerca de casi cualquiera. También tiene muchas formas de dejar Odoo un poco mal configurado. Esta guía te da los comandos exactos para cada paso en Azure.

Para la instalación en sí (Odoo, Postgres, el archivo de configuración y el proxy), sigue nuestra guía sobre Odoo en un VPS. Esta página cubre lo que cambia en Azure.

Puedes ejecutar cada comando az de abajo desde tu propio equipo con la Azure CLI, o desde Azure Cloud Shell en el portal. Un paso, la entrada con Bastion, necesita la CLI en tu propio equipo. Cambia los nombres en mayúsculas por los tuyos. Los ejemplos usan la región westeurope. Usa la tuya.

Qué tamaño de VM elegir

Dos cosas importan más para Odoo:

  • Memoria. El mínimo para empezar es 2 vCPU, 4 GB de memoria y un disco de 50 GB. Con eso corren Odoo y Postgres en una sola VM para un equipo pequeño. Cada worker necesita su parte de memoria, y Postgres quiere la suya. Para más usuarios, importaciones grandes o Azure Database for PostgreSQL, nuestra guía sobre cuántos workers de Odoo necesitas muestra cómo calcularlo.
  • CPU estable. Odoo trabaja todo el día mientras la gente lo usa.

Por qué no una VM de la serie B

Las VMs de la serie B son "burstable". Ganan créditos de CPU mientras están tranquilas y los gastan cuando están ocupadas. Cuando se acaban los créditos, Azure baja la VM a su nivel base hasta que gana más. Un Odoo que se usa todo el día puede quedarse sin créditos en plena jornada. Entonces todas las páginas van lentas.

Así que para producción, elige un tamaño de uso general con CPU fija, por ejemplo:

  • Standard_D2s_v5 (Intel) o Standard_D2as_v5 (AMD). 2 vCPU y 8 GiB de memoria. Una buena opción por defecto.
  • Una generación más nueva, como Standard_D2s_v6, si tu región la tiene.
  • Un tamaño de la serie E (más memoria por CPU) cuando Postgres corre en la misma VM y la base de datos es grande.

Las VMs de la serie B están bien para una copia de prueba que usa poca gente.

Elige un tamaño x86

Azure también vende tamaños Arm, como Dpsv5 y Dpsv6 (Azure Cobalt). Elige mejor un tamaño Intel o AMD. Si quizá nos dejes el trabajo de Odoo más adelante, esto importa: nuestro software bring your own server solo funciona en servidores x86.

Imagen

Usa Ubuntu Server 22.04 LTS o Debian 12. wkhtmltopdf, que Odoo usa para imprimir los PDF, tiene paquetes oficiales para los dos (jammy y bookworm). Imprime una factura antes de salir en vivo.

En la CLI, Ubuntu 22.04 es la imagen Ubuntu2204. Para Debian 12, busca el nombre exacto de la imagen con:

az vm image list --publisher Debian --offer debian-12 --all --output table

Disco: Premium SSD

Usa un Premium SSD (Premium_LRS) para el disco del sistema. Premium SSD v2 es más rápido, pero no puede ser el disco del sistema. Puedes añadir uno más tarde como disco de datos si lo necesitas.

En Premium SSD, la velocidad crece con el tamaño del disco. Un disco de 128 GiB tiene más IOPS que uno de 64 GiB. Deja sitio para el filestore y tres días de copias locales.

Región

Elige la región con cuidado. Tu VM, tu base de datos y tus discos viven en la región que elijas. Pon la VM, la base de datos y el almacenamiento de copias en la misma región, salvo que quieras las copias en una segunda región a propósito.

Red y reglas de seguridad

Crea la red

Todo va en un resource group y en una red virtual (VNet). La VNet tiene tres subredes: una para la VM, una para Azure Bastion y una para la base de datos.

az group create --name odoo-rg --location westeurope

az network vnet create --resource-group odoo-rg --name odoo-vnet \
  --address-prefixes 10.0.0.0/16 \
  --subnet-name odoo-web --subnet-prefixes 10.0.1.0/24

az network vnet subnet create --resource-group odoo-rg --vnet-name odoo-vnet \
  --name AzureBastionSubnet --address-prefixes 10.0.2.0/26

az network vnet subnet create --resource-group odoo-rg --vnet-name odoo-vnet \
  --name odoo-db --address-prefixes 10.0.3.0/27 \
  --delegations Microsoft.DBforPostgreSQL/flexibleServers

Bastion necesita una subred con el nombre exacto AzureBastionSubnet, de tamaño /26 o mayor. La subred de la base de datos está "delegada". Solo Azure Database for PostgreSQL puede usarla. No puedes agrandarla más tarde, así que no bajes de /27. Sáltate la subred odoo-db si Postgres se queda en la VM.

Network security group

Crea un network security group (NSG) y permite solo el tráfico web desde internet:

az network nsg create --resource-group odoo-rg --name odoo-web-nsg

az network nsg rule create --resource-group odoo-rg --nsg-name odoo-web-nsg \
  --name allow-web --priority 100 --direction Inbound --access Allow \
  --protocol Tcp --source-address-prefixes Internet \
  --destination-port-ranges 80 443

az network nsg rule create --resource-group odoo-rg --nsg-name odoo-web-nsg \
  --name allow-ssh-from-bastion --priority 110 --direction Inbound --access Allow \
  --protocol Tcp --source-address-prefixes 10.0.2.0/26 \
  --destination-port-ranges 22

az network vnet subnet update --resource-group odoo-rg --vnet-name odoo-vnet \
  --name odoo-web --network-security-group odoo-web-nsg

El puerto 22 está abierto solo a la subred de Bastion, no a internet. Las reglas por defecto de Azure bloquean todo el resto del tráfico desde internet. Los puertos propios de Odoo (8069 y 8072) siguen cerrados. Solo nginx, en la VM, habla con ellos.

Revisa las reglas:

az network nsg rule list --resource-group odoo-rg --nsg-name odoo-web-nsg --output table

Crea la VM

Una dirección fija

Primero crea una IP pública Standard. Las IPs Standard son siempre estáticas, así que la dirección no cambia cuando la VM se detiene y arranca:

az network public-ip create --resource-group odoo-rg --name odoo-ip --sku Standard
az network public-ip show --resource-group odoo-rg --name odoo-ip \
  --query "{ip:ipAddress, method:publicIPAllocationMethod}" --output table

El método debe decir Static. Apunta el registro DNS de tu dominio a esa dirección antes de pedir un certificado a Let's Encrypt.

Lánzala

az vm create \
  --resource-group odoo-rg --name odoo-vm \
  --image Ubuntu2204 \
  --size Standard_D2s_v5 \
  --vnet-name odoo-vnet --subnet odoo-web \
  --nsg "" \
  --public-ip-address odoo-ip \
  --storage-sku Premium_LRS --os-disk-size-gb 128 \
  --assign-identity \
  --admin-username azureuser --generate-ssh-keys

Qué hacen estas opciones:

  • --nsg "" no añade un segundo NSG en la propia VM. Se aplica el NSG de la subred de arriba.
  • --assign-identity le da a la VM una managed identity asignada por el sistema. Es la propia identidad de la VM en Azure. Le permite escribir copias y enviar datos del disco sin ninguna contraseña ni clave guardada en ella.
  • --generate-ssh-keys guarda una clave de reserva para la cuenta azureuser. No la usarás en el día a día.

Comprueba que la identidad existe, y apunta su ID. Lo necesitas más tarde:

az vm identity show --resource-group odoo-rg --name odoo-vm --query principalId --output tsv

Si una VM ya está corriendo sin identidad, añádele una con:

az vm identity assign --resource-group odoo-rg --name odoo-vm

Comprueba que los puertos de Odoo están cerrados

Cuando Odoo esté en marcha, comprueba desde tu propio equipo que solo responde nginx:

nc -zv -w 5 YOUR_PUBLIC_IP 8069

Esto debe dar timeout. Si se conecta, revisa las reglas del NSG de arriba.

Entra con Bastion, no con un puerto SSH público

Azure Bastion te da una shell en la VM sin abrir el puerto 22 a internet. Entras con tu cuenta de Microsoft Entra ID, así que no hay ninguna clave SSH que compartir o perder.

Activa el acceso con Entra ID en la VM

Instala la extensión de acceso, y date el rol de acceso de admin:

az vm extension set --resource-group odoo-rg --vm-name odoo-vm \
  --publisher Microsoft.Azure.ActiveDirectory --name AADSSHLoginForLinux

az role assignment create \
  --role "Virtual Machine Administrator Login" \
  --assignee "$(az account show --query user.name --output tsv)" \
  --scope "$(az group show --name odoo-rg --query id --output tsv)"

La extensión necesita la managed identity de la VM, que ya añadiste. Ser dueño de la suscripción no basta para entrar. Necesitas este rol.

Crea Bastion

Conectarte desde tu propio terminal necesita el SKU Standard, con "tunneling" (soporte de cliente nativo) activado:

az network public-ip create --resource-group odoo-rg --name odoo-bastion-ip --sku Standard

az network bastion create --resource-group odoo-rg --name odoo-bastion \
  --vnet-name odoo-vnet --public-ip-address odoo-bastion-ip \
  --sku Standard --enable-tunneling true --location westeurope

Tarda unos minutos. Bastion se cobra por cada hora que existe.

Conéctate

Desde tu propio equipo (este comando no funciona en Cloud Shell):

az extension add --name ssh
az login
VM_ID=$(az vm show --resource-group odoo-rg --name odoo-vm --query id --output tsv)
az network bastion ssh --name odoo-bastion --resource-group odoo-rg \
  --target-resource-id "$VM_ID" --auth-type AAD

Entras como tu usuario de Entra ID, y puedes usar sudo.

Dónde va Postgres

Tienes dos buenas opciones:

  • En la misma VM. Simple, y está bien para muchos equipos. Sigue la guía del VPS tal como está.
  • En Azure Database for PostgreSQL (Flexible Server). Azure se encarga del servidor de base de datos, los parches y la restauración a un momento dado. Los pasos están abajo.

Prepara la zona DNS privada

Con acceso privado, la base de datos no tiene dirección pública. Las VMs de la VNet la encuentran a través de una zona DNS privada. Crea la zona y enlázala a la VNet:

az network private-dns zone create --resource-group odoo-rg \
  --name odoo.private.postgres.database.azure.com

az network private-dns link vnet create --resource-group odoo-rg \
  --zone-name odoo.private.postgres.database.azure.com \
  --name odoo-vnet-link --virtual-network odoo-vnet --registration-enabled false

El nombre de la zona debe terminar en postgres.database.azure.com.

Crea el servidor de base de datos

Escribe la contraseña de admin en una variable, para que no quede en el historial de tu shell:

read -s -p "Admin password: " PGADMIN_PW; echo

az postgres flexible-server create \
  --resource-group odoo-rg --name YOUR_DB_SERVER --location westeurope \
  --tier GeneralPurpose --sku-name Standard_D2ds_v5 \
  --version 17 \
  --storage-size 64 --storage-auto-grow Enabled \
  --admin-user odoo_admin --admin-password "$PGADMIN_PW" \
  --vnet odoo-vnet --subnet odoo-db \
  --private-dns-zone odoo.private.postgres.database.azure.com \
  --backup-retention 14

Qué hacen estas opciones:

  • --tier GeneralPurpose evita el nivel Burstable, que tiene los mismos límites de créditos que las VMs de la serie B.
  • --version 17 es un ejemplo. Elige una versión que tu versión de Odoo soporte.
  • --storage-auto-grow Enabled agranda el disco antes de que se llene. Un servidor lleno pasa a solo lectura.
  • --backup-retention 14 guarda 14 días de copias del servidor. El valor por defecto es 7, y el máximo es 35.
  • Añade --geo-redundant-backup Enabled para copiar también las copias a la región emparejada. Solo puedes elegirlo al crear el servidor.
  • Añade --zonal-resiliency Enabled para una copia en espera en otra zona (alta disponibilidad). Las versiones más viejas de la CLI lo llaman --high-availability ZoneRedundant.

Desde la VM, comprueba que el nombre apunta a una dirección privada en 10.0.3.0/27:

getent hosts YOUR_DB_SERVER.postgres.database.azure.com

Permite la extensión pg_trgm

Odoo activa la extensión pg_trgm cuando crea una base de datos. Azure solo permite las extensiones que listas antes:

az postgres flexible-server parameter set --resource-group odoo-rg \
  --server-name YOUR_DB_SERVER --name azure.extensions --value pg_trgm

Si pones unaccent = True en odoo.conf, usa --value pg_trgm,unaccent. Revisa la lista con SHOW azure.extensions; en psql.

Crea el usuario de base de datos odoo

Odoo necesita su propio usuario que pueda crear bases de datos. No uses el usuario admin en el día a día. El usuario admin no es un superusuario. Es miembro de azure_pg_admin, con permisos para crear roles y bases de datos.

En la VM, instala el cliente de Postgres (mira "Usa la misma versión del cliente de Postgres" más abajo) y luego conéctate:

psql "host=YOUR_DB_SERVER.postgres.database.azure.com user=odoo_admin dbname=postgres sslmode=require"
CREATE ROLE odoo WITH LOGIN CREATEDB PASSWORD 'a-long-random-password';

El chat en vivo de Odoo se conecta a la base de datos postgres, no solo a la tuya. Comprueba que el usuario odoo puede entrar allí:

psql "host=YOUR_DB_SERVER.postgres.database.azure.com user=odoo dbname=postgres sslmode=require" -c 'SELECT current_user;'

Deja que Odoo cree yourdb por sí mismo. En su primer arranque con -d yourdb, Odoo crea la base de datos como usuario odoo, así que odoo es su dueño. Revisa el dueño con \l yourdb en psql.

No la crees como admin con CREATE DATABASE yourdb OWNER odoo. Desde Postgres 16, eso falla salvo que el admin tenga permiso para actuar como odoo.

Apunta Odoo al servidor de base de datos

En odoo.conf:

db_host = YOUR_DB_SERVER.postgres.database.azure.com
db_port = 5432
db_user = odoo
db_password = a-long-random-password
db_sslmode = require

Azure activa require_secure_transport por defecto. Rechaza las conexiones sin TLS. db_sslmode = require hace que Odoo lo use siempre.

Límites de conexiones y PgBouncer

Cada proceso de Odoo puede abrir hasta db_maxconn conexiones (64 por defecto). Suma tus workers, los hilos de cron y el proceso del chat, y multiplica por db_maxconn. Deja ese total por debajo del límite del servidor. Un servidor Standard_D2ds_v5 permite 844 conexiones de usuario. Un pequeño Burstable B1ms permite solo 35, así que necesita un db_maxconn mucho más bajo.

Azure trae un PgBouncer integrado en el puerto 6432. Por defecto agrupa en modo transaction. El chat en vivo de Odoo mantiene una conexión LISTEN, y el pooling por transacción la rompe. Deja Odoo en el puerto 5432.

Usa la misma versión del cliente de Postgres

pg_dump no hace copia de un servidor con una versión mayor más nueva que la suya. Si el servidor corre Postgres 17, la VM necesita el cliente de la versión 17. Instálalo desde el repositorio apt de PostgreSQL:

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

Las copias del servidor no cubren el filestore

Azure hace copia del servidor de base de datos y puede restaurarlo a cualquier momento dentro del periodo de retención. No hace copia del filestore. Los adjuntos y las imágenes viven en el disco de la VM, en /var/lib/odoo/filestore. Sigues necesitando la copia en Blob Storage de abajo, para que la base de datos y los archivos se guarden juntos.

Copias de seguridad en Blob Storage

Crea la storage account

Los nombres de storage account se comparten entre todos los clientes de Azure. Usa de 3 a 24 letras minúsculas y números, y haz que sea único:

az storage account create --resource-group odoo-rg --name STORAGE_ACCOUNT \
  --location westeurope --kind StorageV2 --sku Standard_GRS \
  --min-tls-version TLS1_2 \
  --allow-blob-public-access false \
  --allow-shared-key-access false

Qué hacen estas opciones:

  • Standard_GRS guarda una segunda copia en la región emparejada. Comprueba que esa pareja cumple tus normas sobre datos. Si no, usa Standard_ZRS (copias en tres zonas de una sola región).
  • --allow-blob-public-access false significa que ningún contenedor puede hacerse público nunca.
  • --allow-shared-key-access false apaga las claves de la cuenta. Cada petición debe venir de una identidad de Entra ID, como la de la VM. No puede existir una clave robada.

Revísalo:

az storage account show --resource-group odoo-rg --name STORAGE_ACCOUNT \
  --query "{publicAccess:allowBlobPublicAccess, sharedKey:allowSharedKeyAccess}"

Los dos deben ser false.

Activa el versionado y soft delete

El versionado guarda la copia anterior cuando un archivo se sobrescribe o se borra. Soft delete guarda los archivos y versiones borrados durante un número fijo de días:

az storage account blob-service-properties update --resource-group odoo-rg \
  --account-name STORAGE_ACCOUNT \
  --enable-versioning true \
  --enable-delete-retention true --delete-retention-days 14 \
  --enable-container-delete-retention true --container-delete-retention-days 14

Crea el contenedor

az storage container-rm create --resource-group odoo-rg \
  --storage-account STORAGE_ACCOUNT --name your-odoo-backups --enable-vlw

--enable-vlw todavía no bloquea nada. Solo hace posible más tarde el bloqueo opcional de abajo.

Borra las copias viejas de forma programada

Guarda esto como lifecycle.json:

{
  "rules": [
    {
      "enabled": true,
      "name": "expire-old-odoo-backups",
      "type": "Lifecycle",
      "definition": {
        "filters": {
          "blobTypes": ["blockBlob"]
        },
        "actions": {
          "baseBlob": {
            "delete": { "daysAfterModificationGreaterThan": 30 }
          },
          "version": {
            "delete": { "daysAfterCreationGreaterThan": 30 }
          }
        }
      }
    }
  ]
}
az storage account management-policy create --resource-group odoo-rg \
  --account-name STORAGE_ACCOUNT --policy @lifecycle.json

Las copias de más de 30 días se eliminan. Con el versionado activo, "eliminada" primero significa "guardada como versión vieja". La versión vieja se queda 30 días más y luego se va. Después, soft delete la guarda 14 días más. Una política nueva puede tardar hasta 24 horas en empezar. Este comando sustituye cualquier política de ciclo de vida que tuviera la cuenta.

Deja que la VM escriba, y solo escriba

Azure no tiene un rol integrado que solo pueda escribir blobs. Así que creas uno. Permite dos acciones: escribir un blob y añadir uno nuevo. Sin lectura, sin listado, sin borrado. Guarda esto como odoo-backup-writer.json, con el ID de tu suscripción:

{
  "Name": "Odoo Backup Writer",
  "IsCustom": true,
  "Description": "Upload Odoo backups. No read, list or delete.",
  "Actions": [],
  "NotActions": [],
  "DataActions": [
    "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write",
    "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/add/action"
  ],
  "NotDataActions": [],
  "AssignableScopes": [
    "/subscriptions/SUBSCRIPTION_ID/resourceGroups/odoo-rg"
  ]
}

az account show --query id --output tsv muestra el ID de tu suscripción. Crea el rol, y dáselo a la identidad de la VM solo en el contenedor de copias:

az role definition create --role-definition odoo-backup-writer.json

PRINCIPAL_ID=$(az vm identity show --resource-group odoo-rg --name odoo-vm \
  --query principalId --output tsv)
CONTAINER_SCOPE="$(az storage account show --resource-group odoo-rg \
  --name STORAGE_ACCOUNT --query id --output tsv)/blobServices/default/containers/your-odoo-backups"

az role assignment create --assignee-object-id "$PRINCIPAL_ID" \
  --assignee-principal-type ServicePrincipal \
  --role "Odoo Backup Writer" --scope "$CONTAINER_SCOPE"

Un rol nuevo y su asignación pueden tardar unos minutos en funcionar.

La VM puede subir archivos, pero no puede listar, leer ni borrar nada del contenedor. Si alguien entra en el servidor, no puede leer tus copias viejas ni borrarlas. Sobrescribir un archivo está permitido, pero el versionado guarda la copia anterior.

Si tu subida falla con un error de permisos (403), la herramienta de subida necesita algo más que estas dos acciones. En ese caso, dale a la identidad de la VM el rol integrado Storage Blob Data Contributor en el mismo ámbito del contenedor. Ese rol puede leer y borrar los archivos actuales. No puede borrar las versiones viejas, así que el versionado y soft delete siguen guardando tus copias.

Opcional: bloquea las copias durante un tiempo fijo

Si tus normas piden más, una retention policy basada en tiempo bloquea los borrados durante un número fijo de días, incluso para un admin:

az storage container immutability-policy create \
  --account-name STORAGE_ACCOUNT --container-name your-odoo-backups --period 14

La política empieza "unlocked", así que aún puedes cambiarla o quitarla mientras haces pruebas. Una vez que la bloqueas en el portal (Container > Access policy > Lock policy), no puedes quitarla. Deja el periodo más corto que la regla de ciclo de vida de 30 días. Pruébalo antes en una cuenta de sobra.

Instala AzCopy en la VM

curl -sSL -O https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y azcopy

En Debian 12, usa config/debian/12/ en la dirección. AzCopy usa la managed identity de la VM cuando AZCOPY_AUTO_LOGIN_TYPE=MSI está definido. Nunca guardas una clave ni una contraseña en el servidor.

El script de copia

Primero crea la carpeta local:

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

Guarda esto como /usr/local/bin/odoo-backup.sh y ejecuta sudo chmod 755 sobre él:

#!/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"
AZCOPY_AUTO_LOGIN_TYPE=MSI azcopy copy "$DIR/*" "https://STORAGE_ACCOUNT.blob.core.windows.net/your-odoo-backups/$DAY"
find /backups -mindepth 1 -maxdepth 1 -type d -mtime +3 -exec rm -rf {} +

Con Azure Database for PostgreSQL, pg_dump debe conectarse por la red. Cambia la línea de pg_dump por:

pg_dump -h YOUR_DB_SERVER.postgres.database.azure.com -U odoo -Fc "$DB" > "$DIR/$DB.dump"

Luego dale al usuario odoo su contraseña en un archivo .pgpass, en su carpeta personal:

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

El * hace que la misma línea sirva para todas las bases de datos, también la de la prueba de restauración.

Ejecútalo cada noche

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

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

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

Y /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

Actívalo, luego ejecútalo una vez a mano y revisa el resultado:

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

Desde tu propio equipo, comprueba que los archivos llegaron. Tu propia cuenta necesita un rol de datos para verlos, aunque seas dueño de la suscripción. Date acceso de lectura al contenedor una vez:

az role assignment create --role "Storage Blob Data Reader" \
  --assignee "$(az account show --query user.name --output tsv)" \
  --scope "$CONTAINER_SCOPE"

az storage blob list --account-name STORAGE_ACCOUNT \
  --container-name your-odoo-backups --auth-mode login --output table

Prueba una restauración

La VM de producción no puede leer el contenedor. Es a propósito. Así que la prueba de restauración se hace en una segunda VM con su propia identidad y acceso de solo lectura. Crea la VM igual que arriba, con --name odoo-restore-vm y su propia IP pública (o ninguna). Instala Odoo en ella con la guía del VPS, y AzCopy como arriba. Luego dale a su identidad acceso de lectura al contenedor:

RESTORE_ID=$(az vm identity show --resource-group odoo-rg --name odoo-restore-vm \
  --query principalId --output tsv)

az role assignment create --assignee-object-id "$RESTORE_ID" \
  --assignee-principal-type ServicePrincipal \
  --role "Storage Blob Data Reader" --scope "$CONTAINER_SCOPE"

En la VM de prueba, como usuario odoo, descarga la copia de una noche y restáurala:

mkdir -p /tmp/restore && cd /tmp/restore
AZCOPY_AUTO_LOGIN_TYPE=MSI azcopy copy "https://STORAGE_ACCOUNT.blob.core.windows.net/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

Si la copia de prueba usa Azure Database for PostgreSQL, añade -h YOUR_DB_SERVER.postgres.database.azure.com -U odoo a createdb y a pg_restore.

Antes de arrancar Odoo en la copia, neutralízala. Una copia de producción sigue teniendo tus servidores de correo y tus tareas programadas, así que puede enviar correos a clientes reales. Como usuario odoo, ejecuta:

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

Usa odoo-bin en lugar de odoo si ejecutas Odoo desde el código fuente. Esto apaga en la copia los servidores de correo saliente, las acciones programadas y los proveedores de pago. Nuestra guía sobre copiar producción a staging explica lo que cubre y lo que se le escapa.

Luego entra en yourdb_restored y abre algunos adjuntos. Nuestra guía sobre cómo saber si una copia de Odoo se restaurará cubre la prueba completa.

Recibe una alerta antes de que el disco se llene

Los logs, el filestore y las copias locales crecen. Azure no ve lo lleno que está tu disco si la VM no le envía ese dato. El Azure Monitor Agent lo hace. Una data collection rule (DCR) le dice qué enviar y adónde.

Crea un workspace de Log Analytics para guardar los datos, e instala el agente:

az monitor log-analytics workspace create --resource-group odoo-rg \
  --workspace-name odoo-logs --location westeurope

WS_ID=$(az monitor log-analytics workspace show --resource-group odoo-rg \
  --workspace-name odoo-logs --query id --output tsv)
VM_ID=$(az vm show --resource-group odoo-rg --name odoo-vm --query id --output tsv)

az vm extension set --ids "$VM_ID" \
  --name AzureMonitorLinuxAgent --publisher Microsoft.Azure.Monitor \
  --enable-auto-upgrade true

El agente entra con la managed identity de la VM. Guarda esto como odoo-disk-dcr.json. Pon el valor de $WS_ID en lugar de WORKSPACE_ID, y usa tu región:

{
  "location": "westeurope",
  "kind": "Linux",
  "properties": {
    "dataSources": {
      "performanceCounters": [
        {
          "name": "odooDisk",
          "streams": ["Microsoft-Perf"],
          "samplingFrequencyInSeconds": 60,
          "counterSpecifiers": ["\\Logical Disk(*)\\% Free Space"]
        }
      ]
    },
    "destinations": {
      "logAnalytics": [
        {
          "name": "odooLogs",
          "workspaceResourceId": "WORKSPACE_ID"
        }
      ]
    },
    "dataFlows": [
      {
        "streams": ["Microsoft-Perf"],
        "destinations": ["odooLogs"]
      }
    ]
  }
}

Crea la regla y enlázala a la VM:

SUB_ID=$(az account show --query id --output tsv)
DCR_ID="/subscriptions/$SUB_ID/resourceGroups/odoo-rg/providers/Microsoft.Insights/dataCollectionRules/odoo-disk"

az rest --method put \
  --url "https://management.azure.com${DCR_ID}?api-version=2022-06-01" \
  --body @odoo-disk-dcr.json

az monitor data-collection rule association create \
  --name odoo-disk --resource "$VM_ID" --rule-id "$DCR_ID"

Después de unos cinco minutos, comprueba que llegan datos. En el portal, abre la VM, luego Logs, y ejecuta:

Perf
| where ObjectName == "Logical Disk" and CounterName == "% Free Space"
| summarize arg_max(TimeGenerated, CounterValue) by InstanceName

Debes ver / con su espacio libre en porcentaje.

Crea un action group que te envíe un correo, y luego la alerta. Salta cuando el espacio libre en / baja del 20%, es decir, cuando el disco está lleno en más de un 80%:

az monitor action-group create --resource-group odoo-rg \
  --name odoo-alerts --short-name odoo \
  --action email admin [email protected]

az monitor scheduled-query create --resource-group odoo-rg \
  --name odoo-disk-80 --scopes "$VM_ID" \
  --condition "min 'CounterValue' from 'DiskFree' < 20" \
  --condition-query DiskFree="Perf | where ObjectName == 'Logical Disk' and CounterName == '% Free Space' and InstanceName == '/'" \
  --evaluation-frequency 5m --window-size 15m \
  --action-groups odoo-alerts --severity 2

Entérate de cuándo Odoo está caído

Una alerta de disco no te dice que el sitio dejó de responder. Un standard test de Application Insights revisa tu página de acceso desde varios lugares del mundo.

Crea un recurso de Application Insights que use el mismo workspace:

az monitor app-insights component create --resource-group odoo-rg \
  --app odoo-uptime --location westeurope --workspace "$WS_ID"

Luego, en el portal:

  1. Abre odoo-uptime, luego Availability, luego Add Standard test.
  2. Pon la URL https://your-domain/web/login. Deja activados los reintentos y la comprobación del certificado SSL.
  3. Elige al menos cinco ubicaciones de prueba, y guarda.
  4. En el menú de la prueba, elige Open Rules (Alerts) page. Edita la regla y añade el action group odoo-alerts. Sin esto, solo ves las alertas en el portal, sin correo.

Correo saliente

Azure bloquea el correo saliente por el puerto 25 desde las VMs, salvo que la suscripción sea un Enterprise Agreement o un Microsoft Customer Agreement para empresas. El correo de Odoo enviado así dará timeout. Envía a través de un servicio SMTP en el puerto 587. Azure no bloquea el puerto 587, sea cual sea tu tipo de suscripción. Tu proveedor de correo actual sirve.

Azure Communication Services también puede enviar correo por SMTP:

  1. Crea un recurso Email Communication Services, añade tu dominio y verifícalo con los registros DNS que te da. Conéctalo a un recurso Communication Services.
  2. Registra una app en Microsoft Entra ID y crea un client secret para ella.
  3. Dale a esa app el rol Communication and Email Service Owner en el recurso Communication Services.
  4. En el recurso Communication Services, abre SMTP Usernames y añade un nombre de usuario enlazado a la app.
  5. En Odoo, activa el modo desarrollador. Ve a Settings > Technical > Outgoing Mail Servers. Pon el servidor smtp.azurecomm.net, el puerto 587 y el cifrado TLS (STARTTLS). El usuario es el SMTP username del paso 4. La contraseña es el client secret de la app.

Trampas de Odoo en cualquier servidor, Azure incluido

Websocket. Desde Odoo 16, el chat y las actualizaciones en vivo usan el puerto 8072. nginx debe enviar /websocket allí, con las cabeceras de upgrade.

Proxy mode. Odoo solo confía en las cabeceras reenviadas cuando proxy_mode = True y la petición trae X-Forwarded-Host. Sin las dos cosas, tus enlaces salen con http://.

Gestor de bases de datos. Pon list_db = False y un admin_passwd fuerte.

Workers. Pon workers por encima de 0, ajustado a la memoria. Así Odoo arranca el worker del chat en gevent_port.

En odoo.conf:

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

En nginx, junto a tu bloque location /:

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;
}

El bloque map va en la sección http, fuera de server.

Detrás de Application Gateway

No necesitas un balanceador de carga para una sola VM. Si usas Application Gateway de todos modos:

  • Application Gateway gestiona los websockets por su cuenta. Deja nginx detrás de él, para que /websocket siga yendo al 8072.
  • Envía X-Forwarded-Proto y X-Forwarded-For, y el host original en X-Original-Host. No envía X-Forwarded-Host, y sin ella Odoo ignora las otras. Así que nginx debe añadirla.
  • Termina el HTTPS, así que nginx ve HTTP plano. Si el gateway solo escucha en HTTPS, dile a Odoo que la petición era HTTPS.

En los dos bloques location de nginx, usa:

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

$host funciona si el gateway mantiene el nombre de host original, que es lo que hace por defecto. Luego permite el puerto 80 en odoo-web-nsg solo desde la subred del gateway, no desde todo internet.

Antes de salir en vivo

  • La VM es de un tamaño x86 de uso general (no de la serie B), con un disco Premium SSD
  • Odoo corre con workers por encima de 0, ajustado a la memoria del servidor
  • /websocket va al puerto 8072, y el chat funciona
  • proxy_mode = True, y los enlaces de los correos empiezan por https://
  • list_db = False y un admin_passwd fuerte
  • Solo están abiertos a internet los puertos 80 y 443, el puerto 8069 da timeout, y entras a través de Bastion
  • Con Azure Database for PostgreSQL: acceso privado, db_sslmode = require, y pg_trgm permitida
  • El certificado SSL se renueva solo
  • Los PDF se imprimen (wkhtmltopdf funciona)
  • Los correos de prueba llegan, enviados por el puerto 587
  • Los logs rotan, el correo de la alerta de disco funciona, y el uptime test está en verde
  • El timer de la copia se ejecutó, y los archivos están en Blob Storage
  • Una copia se ha restaurado en otra VM, y los adjuntos se abren

Quédate con Azure, déjanos el trabajo de Odoo

Puedes mantener Odoo en tu suscripción de Azure y aun así no gestionarlo tú mismo. Con bring your own server, la VM sigue en tu suscripción y en la región que elijas. Nosotros hacemos funcionar Odoo en ella: despliegues desde Git, staging, copias de seguridad, actualizaciones y ayuda de expertos en Odoo. Tú mantienes la VM en marcha y con el tamaño suficiente. Nuestro software se conecta desde tu VM hacia fuera, así que no nos abres ningún puerto.

Skysize está hecho por ex ingenieros de Odoo y tiene la certificación ISO 27001.

Habla con un experto en hosting de Odoo

Preguntas frecuentes

¿Puede Odoo correr en Azure? Sí. Usa una VM x86 de uso general con un disco Premium SSD, Postgres en la VM o en Azure Database for PostgreSQL, y copias de seguridad en Blob Storage.

¿Puedo usar Azure Database for PostgreSQL para Odoo? Sí. Usa acceso privado, permite la extensión pg_trgm, y crea para Odoo un usuario de base de datos que pueda crear bases. Conéctate por el puerto 5432, no a través de PgBouncer. Las copias del servidor no cubren el filestore, así que cópialo también.

¿Qué tamaño de VM de Azure es el mejor para Odoo? Uno con suficiente memoria para tus workers y Postgres, y una CPU estable. Un tamaño de la serie D como Standard_D2s_v5 es un buen comienzo. Evita las VMs de la serie B en producción.

¿Puede Odoo correr en VMs Arm de Azure? Odoo en sí puede, pero comprueba que wkhtmltopdf funciona antes de salir en vivo. Nuestro software bring your own server solo funciona en x86, así que elige un tamaño Intel o AMD si quizá lo uses.

¿Qué permisos necesita la VM de Odoo para las copias? Solo escribir y añadir blobs en tu contenedor de copias, a través de un rol personalizado en la managed identity de la VM. Las restauraciones usan una segunda VM con Storage Blob Data Reader.

¿Tengo que abrir el puerto 22? No a internet. Azure Bastion se conecta a la VM desde dentro de la VNet, y entras con Entra ID.

¿Por qué Odoo no envía correo desde mi VM de Azure? Azure bloquea el puerto 25 en la mayoría de los tipos de suscripción. Envía a través de tu proveedor de correo o de Azure Communication Services en el puerto 587.

Odoo en Azure: cómo alojarlo bien
Escribe sobre la infraestructura de Odoo en Skysize, un proveedor de hosting de Odoo con sede en Bélgica, creado por exingenieros de Odoo y certificado ISO 27001, que sirve a empresas y agencias en todo el mundo.