[UPDATE 2026] Migrer Odoo 17 en Docker : standalone (venv) → Docker Compose (4 instances) + reverse proxy MyVestaCP

Published by David on

Odoo 17 en Docker — migration standalone vers Docker Compose

Quatrième étape : migrer Odoo 17 d’une installation standalone (venv Python 3.12 + PostgreSQL 13) vers Docker Compose avec 4 instances et reverse proxy MyVestaCP.

Document paramétrable

Ce guide utilise des variables {{...}} à remplacer par vos propres valeurs.
Vous pouvez aussi donner ce document à une IA avec vos paramètres pour générer une version personnalisée.

Variable Description Exemple
{{IP_ANCIEN}} IP de l’ancien serveur 203.0.113.10
{{IP_NOUVEAU}} IP du nouveau serveur 198.51.100.20
{{HOSTNAME_ANCIEN}} Hostname de l’ancien serveur vps-ancien
{{HOSTNAME_NOUVEAU}} Hostname du nouveau serveur vps-nouveau
{{PORT_SSH_ANCIEN}} Port SSH de l’ancien serveur 22
{{PORT_SSH_NOUVEAU}} Port SSH du nouveau serveur 22
{{DOMAINE_ODOO_1}} Sous-domaine de l’instance prod principale odoo-prod.exemple.com
{{DOMAINE_ODOO_2}} Sous-domaine de l’instance prod secondaire odoo-client2.exemple.com
{{DOMAINE_ODOO_TEST}} Sous-domaine de l’instance test odoo-test.exemple.com
{{DOMAINE_ODOO_ADMIN}} Sous-domaine de l’instance admin odoo-admin.exemple.com
{{ALIAS_ODOO_1}} Alias du domaine principal (optionnel) erp.mondomaine.com
{{INSTANCE_1}} Nom de l’instance/BDD prod principale prod
{{INSTANCE_2}} Nom de l’instance/BDD prod secondaire client2
{{DB_PASSWORD}} Mot de passe PostgreSQL MotDePassePostgres
{{ODOO_ADMIN_PASSWORD}} Mot de passe admin (master password) Odoo MotDePasseOdooAdmin
{{DB_TEST_OLD_NAME}} Ancien nom de la BDD de test (sur l’ancien serveur) prod-20260101-test
{{USER_VESTA}} Utilisateur VestaCP admin

Note : les noms d’instances ({{INSTANCE_1}}, {{INSTANCE_2}})
sont utilisés comme noms de base de données, noms de conteneurs Docker et dans les dbfilter.
Choisissez des noms courts, sans espaces ni caractères spéciaux.

Introduction

Sur l’ancien serveur ({{HOSTNAME_ANCIEN}}, IP {{IP_ANCIEN}}, SSH port {{PORT_SSH_ANCIEN}}),
Odoo 17 fonctionne en installation standalone : un environnement virtuel Python 3.12 et PostgreSQL 13 installés directement sur le système.

Sur le nouveau serveur ({{HOSTNAME_NOUVEAU}}, IP {{IP_NOUVEAU}}, SSH port {{PORT_SSH_NOUVEAU}}),
nous migrons vers une architecture Docker Compose avec 4 instances Odoo 17 partageant une seule base de données PostgreSQL 13.

Architecture cible

  • 4 instances Odoo 17 :
    • {{INSTANCE_1}} (port 8069) — instance de production principale
    • {{INSTANCE_2}} (port 8169) — instance de production secondaire
    • test (port 8269) — environnement de test
    • admin (port 8369) — interface d’administration
  • 1 conteneur PostgreSQL 13 partagé entre toutes les instances
  • MyVestaCP gère le reverse proxy Nginx et les certificats SSL (Let’s Encrypt)
  • Fichiers Docker : /home/docker/odoo17/

Avantages de cette architecture :
isolation des instances (une mise à jour de module n’affecte pas les autres),
multi-tenant (plusieurs bases de données, plusieurs clients),
facilité de déploiement (backup, scale, rollback via Docker),
et SSL/proxy géré par VestaCP (pas besoin de Traefik ou reverse proxy custom).

1) Créer les domaines dans MyVestaCP

Chaque instance Odoo nécessite un domaine (ou sous-domaine) distinct.
On crée les 4 domaines dans le panel MyVestaCP avant de configurer le proxy.

1.1 Créer les domaines

Dans le panel MyVestaCP → WEBAdd Web Domain :

  • {{DOMAINE_ODOO_1}}
  • {{DOMAINE_ODOO_2}}
  • {{DOMAINE_ODOO_TEST}}
  • {{DOMAINE_ODOO_ADMIN}}

Pour chaque domaine :

  • DNS Support : décocher (le DNS est géré ailleurs, par exemple OVH)
  • Mail Support : décocher (Odoo n’a pas besoin de serveur mail ici)
  • Advanced options : laisser les valeurs par défaut

1.2 Ajouter un alias au domaine principal

Le domaine {{DOMAINE_ODOO_1}} a un alias {{ALIAS_ODOO_1}}.
Dans le panel → éditer le domaine → champ Aliases : ajouter {{ALIAS_ODOO_1}}.

Important : ne pas activer Let’s Encrypt pour l’instant — le DNS ne pointe pas encore vers le nouveau serveur.
On activera SSL après avoir basculé les enregistrements DNS (section 12).

2) Créer les templates Nginx pour reverse proxy Odoo

Par défaut, MyVestaCP sert les fichiers statiques depuis /home/{{USER_VESTA}}/web/domaine/public_html/.
Pour Odoo, on veut qu’il proxy les requêtes vers les conteneurs Docker (ports 8069, 8169, 8269, 8369).

La solution : créer des templates Nginx personnalisés qui indiquent à VestaCP de faire du reverse proxy
au lieu de servir des fichiers. On aura besoin de deux templates par port :

  • odoo-XXXX.tpl (HTTP, port 80)
  • odoo-XXXX.stpl (HTTPS, port 443)

2.1 Template HTTP : odoo-8069.tpl

Créer le fichier /usr/local/vesta/data/templates/web/nginx/odoo-8069.tpl :

nano /usr/local/vesta/data/templates/web/nginx/odoo-8069.tpl

Contenu :

server {
    listen      %ip%:%proxy_port%;
    server_name %domain_idn% %alias_idn%;

    error_log   /var/log/%web_system%/domains/%domain%.error.log error;

    location /.well-known/acme-challenge {
        default_type text/plain;
        root /home/%user%/web/%domain%/public_html;
    }

    location /longpolling {
        proxy_pass http://127.0.0.1:8072;
        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;
    }

    location / {
        proxy_pass http://127.0.0.1:8069;
        proxy_redirect off;
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_read_timeout 720s;
        proxy_connect_timeout 720s;
        proxy_send_timeout 720s;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location ~* /web/static/ {
        proxy_pass http://127.0.0.1:8069;
        proxy_cache_valid 200 90m;
        proxy_buffering on;
        expires 864000;
    }

    include %home%/%user%/conf/web/nginx.%domain%.conf*;
}

Le location /.well-known/acme-challenge est nécessaire pour que Let’s Encrypt puisse valider le domaine
avant d’émettre un certificat SSL (challenge HTTP-01).

2.2 Template HTTPS : odoo-8069.stpl

Créer le fichier /usr/local/vesta/data/templates/web/nginx/odoo-8069.stpl :

nano /usr/local/vesta/data/templates/web/nginx/odoo-8069.stpl

Contenu :

server {
    listen      %ip%:%proxy_ssl_port% ssl;
    http2 on;
    server_name %domain_idn% %alias_idn%;

    ssl_certificate      %ssl_pem%;
    ssl_certificate_key  %ssl_key%;

    error_log   /var/log/%web_system%/domains/%domain%.error.log error;

    location /longpolling {
        proxy_pass http://127.0.0.1:8072;
        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;
    }

    location / {
        proxy_pass http://127.0.0.1:8069;
        proxy_redirect off;
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_read_timeout 720s;
        proxy_connect_timeout 720s;
        proxy_send_timeout 720s;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location ~* /web/static/ {
        proxy_pass http://127.0.0.1:8069;
        proxy_cache_valid 200 90m;
        proxy_buffering on;
        expires 864000;
    }

    include %home%/%user%/conf/web/nginx.%domain%.ssl.conf*;
}

Important : utiliser listen %ip%:%proxy_ssl_port% ssl; suivi de http2 on; sur deux lignes distinctes.
La syntaxe listen ... ssl http2 (sur une seule ligne) est dépréciée dans Nginx 1.25+ et génère un warning.

On ne met pas les directives ssl_stapling dans le template initial — elles causent des warnings
avec les certificats auto-signés. VestaCP les ajoutera automatiquement lors de l’activation de Let’s Encrypt.

2.3 Dupliquer les templates pour les autres ports

On duplique les templates pour les ports 8169, 8269 et 8369 en utilisant sed :

# Port 8169 ({{INSTANCE_2}})
sed 's/8069/8169/g; s/8072/8172/g' \
  /usr/local/vesta/data/templates/web/nginx/odoo-8069.tpl \
  > /usr/local/vesta/data/templates/web/nginx/odoo-8169.tpl

sed 's/8069/8169/g; s/8072/8172/g' \
  /usr/local/vesta/data/templates/web/nginx/odoo-8069.stpl \
  > /usr/local/vesta/data/templates/web/nginx/odoo-8169.stpl

# Port 8269 (test)
sed 's/8069/8269/g; s/8072/8272/g' \
  /usr/local/vesta/data/templates/web/nginx/odoo-8069.tpl \
  > /usr/local/vesta/data/templates/web/nginx/odoo-8269.tpl

sed 's/8069/8269/g; s/8072/8272/g' \
  /usr/local/vesta/data/templates/web/nginx/odoo-8069.stpl \
  > /usr/local/vesta/data/templates/web/nginx/odoo-8269.stpl

# Port 8369 (admin)
sed 's/8069/8369/g; s/8072/8372/g' \
  /usr/local/vesta/data/templates/web/nginx/odoo-8069.tpl \
  > /usr/local/vesta/data/templates/web/nginx/odoo-8369.tpl

sed 's/8069/8369/g; s/8072/8372/g' \
  /usr/local/vesta/data/templates/web/nginx/odoo-8069.stpl \
  > /usr/local/vesta/data/templates/web/nginx/odoo-8369.stpl

2.4 Appliquer les templates aux domaines

Appliquer chaque template au domaine correspondant avec la commande v-change-web-domain-proxy-tpl :

/usr/local/vesta/bin/v-change-web-domain-proxy-tpl {{USER_VESTA}} {{DOMAINE_ODOO_1}} odoo-8069
/usr/local/vesta/bin/v-change-web-domain-proxy-tpl {{USER_VESTA}} {{DOMAINE_ODOO_2}} odoo-8169
/usr/local/vesta/bin/v-change-web-domain-proxy-tpl {{USER_VESTA}} {{DOMAINE_ODOO_TEST}} odoo-8269
/usr/local/vesta/bin/v-change-web-domain-proxy-tpl {{USER_VESTA}} {{DOMAINE_ODOO_ADMIN}} odoo-8369

2.5 Vérifier et recharger Nginx

nginx -t
systemctl reload nginx

Si nginx -t affiche syntax is ok et test is successful, la configuration est correcte.

2bis) Troubleshooting : warnings Nginx courants

Warning : listen ... http2 deprecated

Si tu vois ce warning lors du nginx -t :

nginx: [warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead

C’est que le template utilise l’ancienne syntaxe listen ... ssl http2 sur une seule ligne.
Modifier le template pour séparer http2 sur sa propre ligne :

listen      %ip%:%proxy_ssl_port% ssl;
http2 on;

Warning : ssl_stapling ignored, issuer certificate not found

Avant l’activation de Let’s Encrypt, le serveur utilise un certificat auto-signé.
OCSP stapling ne peut pas fonctionner avec un certificat auto-signé, d’où le warning.
Ce warning disparaîtra automatiquement après l’activation de Let’s Encrypt (section 13).

Si tu veux éviter ce warning avant Let’s Encrypt, retire les lignes ssl_stapling on; et ssl_stapling_verify on;
du template .stpl. Elles seront automatiquement ajoutées par VestaCP lors de l’activation de Let’s Encrypt.

3) Créer la structure des répertoires Docker

On crée une structure de dossiers pour organiser la configuration Odoo, les modules personnalisés et les backups :

mkdir -p /home/docker/odoo17/config
mkdir -p /home/docker/odoo17/ext_addons
mkdir -p /home/docker/odoo17/backup
  • config/ : fichiers de configuration Odoo (odoo-{{INSTANCE_1}}.conf, etc.)
  • ext_addons/ : modules personnalisés (account-reconcile, prestashop_connector_basic, etc.)
  • backup/ : exports temporaires (dumps SQL, tar filestore)

3.1 Installer Docker Compose v2 (plugin)

Le paquet docker.io des dépôts Debian n’inclut pas le plugin Docker Compose v2.
La commande docker compose (avec espace) ne fonctionnera pas sans l’installer manuellement.

Attention : apt install docker-compose-plugin ne fonctionne pas
car ce paquet n’existe que dans le dépôt officiel Docker (docker.com), pas dans les dépôts Debian.
On installe donc le binaire manuellement.

# Créer le répertoire des plugins Docker CLI
mkdir -p /usr/local/lib/docker/cli-plugins

# Télécharger Docker Compose v2
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 \
  -o /usr/local/lib/docker/cli-plugins/docker-compose

# Rendre le binaire exécutable
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

# Vérifier l'installation
docker compose version

Tu devrais voir quelque chose comme Docker Compose version v2.x.x.
Toutes les commandes docker compose de ce guide utilisent la syntaxe v2 (avec espace, sans tiret).

Docker Compose v1 (docker-compose avec tiret) est déprécié depuis juillet 2023.
Si l’ancien docker-compose est installé sur le serveur, il n’est pas nécessaire de le désinstaller — les deux coexistent sans conflit.

4) Uploader les fichiers de configuration

On uploade les fichiers de configuration sur le serveur via le FTP de MyVestaCP
(le domaine {{DOMAINE_ODOO_ADMIN}} a déjà été créé dans le panel),
puis on les déplace vers /home/docker/odoo17/ via SSH.

Pourquoi pas SFTP ? Si le serveur a le 2FA (TOTP) activé,
FileZilla ne peut pas gérer l’authentification multi-facteur (clé SSH + code TOTP).
Le FTP de VestaCP contourne ce problème. Alternative : utiliser WinSCP,
qui supporte nativement le 2FA.

4.1 Upload via le FTP VestaCP

Ouvrir FileZilla → FichierGestionnaire de sitesNouveau site :

  • Protocole : FTP - File Transfer Protocol
  • Hôte : {{DOMAINE_ODOO_ADMIN}} (ou l’IP du serveur)
  • Port : 21 (par défaut)
  • Type d’authentification : Normale
  • Utilisateur : l’utilisateur FTP du domaine (créé par VestaCP, ex : {{USER_VESTA}})
  • Mot de passe : le mot de passe FTP du panel

Uploader les 5 fichiers suivants à la racine (/public_html/) :

Fichier local Destination finale
docker-compose.yml /home/docker/odoo17/docker-compose.yml
odoo-{{INSTANCE_1}}.conf /home/docker/odoo17/config/odoo-{{INSTANCE_1}}.conf
odoo-{{INSTANCE_2}}.conf /home/docker/odoo17/config/odoo-{{INSTANCE_2}}.conf
odoo-test.conf /home/docker/odoo17/config/odoo-test.conf
odoo-admin.conf /home/docker/odoo17/config/odoo-admin.conf

4.2 Déplacer les fichiers via SSH

Les fichiers uploadés par FTP atterrissent dans /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/.
On les déplace vers leur emplacement final :

# Déplacer le docker-compose.yml
sudo mv /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/docker-compose.yml \
  /home/docker/odoo17/docker-compose.yml

# Déplacer les fichiers de configuration Odoo
sudo mv /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/odoo-{{INSTANCE_1}}.conf \
  /home/docker/odoo17/config/odoo-{{INSTANCE_1}}.conf
sudo mv /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/odoo-{{INSTANCE_2}}.conf \
  /home/docker/odoo17/config/odoo-{{INSTANCE_2}}.conf
sudo mv /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/odoo-test.conf \
  /home/docker/odoo17/config/odoo-test.conf
sudo mv /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/odoo-admin.conf \
  /home/docker/odoo17/config/odoo-admin.conf

4.3 Permissions

Le docker-compose.yml contient des mots de passe — on restreint sa lecture à root.
Les fichiers .conf doivent rester lisibles par le conteneur Odoo (UID 101).

# Propriétaire : root pour tout
sudo chown -R root:root /home/docker/odoo17/

# docker-compose.yml : lecture root uniquement (contient des mots de passe)
sudo chmod 600 /home/docker/odoo17/docker-compose.yml

# Fichiers .conf : lisibles par tous (UID 101 dans le conteneur doit pouvoir les lire)
sudo chmod 644 /home/docker/odoo17/config/*.conf

# Répertoires : traversables
sudo chmod 755 /home/docker/odoo17/
sudo chmod 755 /home/docker/odoo17/config/
sudo chmod -R 755 /home/docker/odoo17/ext_addons/

Le FTP de VestaCP pourra être réutilisé à l’avenir pour uploader de nouveaux modules Odoo.
Uploader dans /home/{{USER_VESTA}}/web/{{DOMAINE_ODOO_ADMIN}}/public_html/,
puis déplacer vers /home/docker/odoo17/ext_addons/ via SSH
(voir section « Future : ajouter un module Odoo »).

4.4 Contenu du docker-compose.yml

version: '3.8'

services:
  db:
    image: postgres:13
    container_name: odoo17-db
    restart: always
    environment:
      POSTGRES_USER: odoo17
      POSTGRES_PASSWORD: {{DB_PASSWORD}}
      POSTGRES_DB: postgres
    volumes:
      - odoo17-db-data:/var/lib/postgresql/data
    networks:
      - odoo17-network

  odoo-{{INSTANCE_1}}:
    image: odoo:17
    container_name: odoo17-{{INSTANCE_1}}
    restart: always
    depends_on:
      - db
    ports:
      - "127.0.0.1:8069:8069"
      - "127.0.0.1:8072:8072"
    volumes:
      - odoo17-data:/var/lib/odoo
      - ./config/odoo-{{INSTANCE_1}}.conf:/etc/odoo/odoo.conf:ro
      - ./ext_addons:/mnt/extra-addons
    environment:
      - HOST=db
      - USER=odoo17
      - PASSWORD={{DB_PASSWORD}}
    networks:
      - odoo17-network

  odoo-{{INSTANCE_2}}:
    image: odoo:17
    container_name: odoo17-{{INSTANCE_2}}
    restart: always
    depends_on:
      - db
    ports:
      - "127.0.0.1:8169:8069"
      - "127.0.0.1:8172:8072"
    volumes:
      - odoo17-data:/var/lib/odoo
      - ./config/odoo-{{INSTANCE_2}}.conf:/etc/odoo/odoo.conf:ro
      - ./ext_addons:/mnt/extra-addons
    environment:
      - HOST=db
      - USER=odoo17
      - PASSWORD={{DB_PASSWORD}}
    networks:
      - odoo17-network

  odoo-test:
    image: odoo:17
    container_name: odoo17-test
    restart: always
    depends_on:
      - db
    ports:
      - "127.0.0.1:8269:8069"
      - "127.0.0.1:8272:8072"
    volumes:
      - odoo17-data:/var/lib/odoo
      - ./config/odoo-test.conf:/etc/odoo/odoo.conf:ro
      - ./ext_addons:/mnt/extra-addons
    environment:
      - HOST=db
      - USER=odoo17
      - PASSWORD={{DB_PASSWORD}}
    networks:
      - odoo17-network

  odoo-admin:
    image: odoo:17
    container_name: odoo17-admin
    restart: always
    depends_on:
      - db
    ports:
      - "127.0.0.1:8369:8069"
      - "127.0.0.1:8372:8072"
    volumes:
      - odoo17-data:/var/lib/odoo
      - ./config/odoo-admin.conf:/etc/odoo/odoo.conf:ro
      - ./ext_addons:/mnt/extra-addons
    environment:
      - HOST=db
      - USER=odoo17
      - PASSWORD={{DB_PASSWORD}}
    networks:
      - odoo17-network

volumes:
  odoo17-db-data:
    name: odoo17-db-data
  odoo17-data:
    name: odoo17-data

networks:
  odoo17-network:
    name: odoo17-network

Les ports sont bindés sur 127.0.0.1 (localhost uniquement) — seul Nginx (reverse proxy) peut y accéder.
Les instances ne sont pas exposées directement sur Internet, ce qui améliore la sécurité.

4.5 Contenu des fichiers de configuration Odoo

odoo-{{INSTANCE_1}}.conf

[options]
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
admin_passwd = {{ODOO_ADMIN_PASSWORD}}
db_host = db
db_port = 5432
db_user = odoo17
db_password = {{DB_PASSWORD}}
dbfilter = ^{{INSTANCE_1}}$
http_port = 8069
longpolling_port = 8072
workers = 2
max_cron_threads = 1
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200
server_wide_modules = base,web

odoo-{{INSTANCE_2}}.conf

[options]
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
admin_passwd = {{ODOO_ADMIN_PASSWORD}}
db_host = db
db_port = 5432
db_user = odoo17
db_password = {{DB_PASSWORD}}
dbfilter = ^{{INSTANCE_2}}$
http_port = 8069
longpolling_port = 8072
workers = 2
max_cron_threads = 1
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200
server_wide_modules = base,web

odoo-test.conf

[options]
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
admin_passwd = {{ODOO_ADMIN_PASSWORD}}
db_host = db
db_port = 5432
db_user = odoo17
db_password = {{DB_PASSWORD}}
dbfilter = ^{{INSTANCE_1}}-test$
http_port = 8069
longpolling_port = 8072
workers = 1
max_cron_threads = 1
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200
server_wide_modules = base,web

odoo-admin.conf

[options]
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
admin_passwd = {{ODOO_ADMIN_PASSWORD}}
db_host = db
db_port = 5432
db_user = odoo17
db_password = {{DB_PASSWORD}}
dbfilter = .*
http_port = 8069
longpolling_port = 8072
workers = 1
max_cron_threads = 1
list_db = True
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200
server_wide_modules = base,web

Le paramètre dbfilter contrôle quelle(s) base(s) de données l’instance peut voir :
^{{INSTANCE_1}}$ = uniquement la base « {{INSTANCE_1}} »,
.* = toutes les bases (mode admin).
Cela permet d’isoler les instances : un utilisateur sur {{DOMAINE_ODOO_1}}
ne verra jamais la base « {{INSTANCE_2}} » ni les autres.

5) Exporter les données depuis l’ancien serveur

Sur l’ancien serveur, on exporte :

  • Les bases de données Odoo (uniquement celles nécessaires, pas tout le serveur)
  • Le filestore (fichiers uploadés dans Odoo : images, PDF, pièces jointes)
  • Les modules personnalisés (addons custom)

5.1 Export des bases de données

On exporte uniquement les bases Odoo nécessaires avec pg_dump (une par base).

Ne pas utiliser pg_dumpall : il exporte toutes les bases et tous les rôles du serveur
(y compris ceux d’autres applications comme ownCloud, etc.), ce qui crée des bases et rôles parasites
dans le conteneur Docker. Le rôle odoo17 est déjà créé automatiquement par Docker Compose
(POSTGRES_USER), donc pas besoin d’exporter les rôles.

# Sur l'ANCIEN serveur (en root ou avec sudo)
sudo -u postgres pg_dump {{INSTANCE_1}} | gzip > /root/odoo17-db-{{INSTANCE_1}}.sql.gz
sudo -u postgres pg_dump {{INSTANCE_2}} | gzip > /root/odoo17-db-{{INSTANCE_2}}.sql.gz
sudo -u postgres pg_dump "{{DB_TEST_OLD_NAME}}" | gzip > /root/odoo17-db-test.sql.gz

Les bases à exporter sont celles qui correspondent aux dbfilter des instances Odoo.
Ici : {{INSTANCE_1}} (production), {{INSTANCE_2}} (production secondaire),
et {{DB_TEST_OLD_NAME}} (test, sera renommée en {{INSTANCE_1}}-test).

5.2 Export du filestore

Le filestore Odoo contient tous les fichiers uploadés (images produits, factures PDF, pièces jointes).
Il se trouve dans ~/.local/share/Odoo/filestore/ de l’utilisateur qui exécute Odoo.

# Trouver le répertoire filestore
sudo find /home /root -type d -name filestore 2>/dev/null

# Archiver le filestore (adapter le chemin trouvé ci-dessus)
sudo tar -czf /root/odoo17-filestore.tar.gz \
  -C /chemin/vers/.local/share/Odoo/ filestore/

On utilise /root/ au lieu de /tmp/ car ce dernier peut avoir
des restrictions d’écriture sur certains serveurs (permissions, mount flags).

5.3 Export des modules personnalisés

Archiver les modules custom installés sur l’ancien serveur.
Exemple avec plusieurs dossiers de modules :

cd /opt/odoo17/custom_addons/
tar -czf /root/odoo17-custom-addons.tar.gz \
  module1/ \
  module2/ \
  ...

6) Transférer les exports via rsync

Depuis le nouveau serveur, récupérer les archives créées sur l’ancien serveur :

# Depuis le NOUVEAU serveur
rsync -avz -e "ssh -p {{PORT_SSH_ANCIEN}}" \
  root@{{IP_ANCIEN}}:/root/odoo17-*.tar.gz \
  /home/docker/odoo17/backup/

rsync -avz -e "ssh -p {{PORT_SSH_ANCIEN}}" \
  root@{{IP_ANCIEN}}:/root/odoo17-db-{{INSTANCE_1}}.sql.gz \
  root@{{IP_ANCIEN}}:/root/odoo17-db-{{INSTANCE_2}}.sql.gz \
  root@{{IP_ANCIEN}}:/root/odoo17-db-test.sql.gz \
  /home/docker/odoo17/backup/

Si la connexion SSH échoue, vérifier que la clé SSH a bien été configurée (voir article précédent, section 1.1).

7) Extraire les modules personnalisés

cd /home/docker/odoo17/
tar -xzf backup/odoo17-custom-addons.tar.gz -C ext_addons/

Les modules sont maintenant dans /home/docker/odoo17/ext_addons/.
Ils seront montés dans les conteneurs via docker-compose.yml (volume ./ext_addons:/mnt/extra-addons).

8) Démarrer PostgreSQL et restaurer les bases de données

8.1 Démarrer le conteneur PostgreSQL

cd /home/docker/odoo17/
docker compose up -d db

Attendre que PostgreSQL soit prêt :

docker compose exec db pg_isready -U odoo17

Quand tu vois ... accepting connections, c’est prêt.

8.2 Restaurer les bases de données

Pas besoin de restaurer les rôles PostgreSQL : le rôle odoo17 est déjà créé
automatiquement par Docker Compose via POSTGRES_USER: odoo17.

On crée d’abord les bases vides, puis on restaure chaque dump :

# Créer les bases vides
docker compose exec db psql -U odoo17 -d postgres -c "CREATE DATABASE {{INSTANCE_1}};"
docker compose exec db psql -U odoo17 -d postgres -c "CREATE DATABASE {{INSTANCE_2}};"
docker compose exec db psql -U odoo17 -d postgres -c 'CREATE DATABASE "{{INSTANCE_1}}-test";'

# Restaurer chaque base
gunzip -c /home/docker/odoo17/backup/odoo17-db-{{INSTANCE_1}}.sql.gz | \
  docker compose exec -T db psql -U odoo17 -d {{INSTANCE_1}}

gunzip -c /home/docker/odoo17/backup/odoo17-db-{{INSTANCE_2}}.sql.gz | \
  docker compose exec -T db psql -U odoo17 -d {{INSTANCE_2}}

gunzip -c /home/docker/odoo17/backup/odoo17-db-test.sql.gz | \
  docker compose exec -T db psql -U odoo17 -d {{INSTANCE_1}}-test

La base de test est exportée sous le nom {{DB_TEST_OLD_NAME}} sur l’ancien serveur,
mais on la restaure directement dans {{INSTANCE_1}}-test sur le nouveau serveur
(pour correspondre au dbfilter de l’instance test). Pas besoin de renommer après coup.

8.4 Vérifier les bases de données

docker compose exec db psql -U odoo17 -d postgres -c "\l"

Tu devrais voir :

  • {{INSTANCE_1}}
  • {{INSTANCE_2}}
  • {{INSTANCE_1}}-test
  • postgres (base système)

9) Restaurer le filestore

Le filestore doit être copié dans le volume Docker odoo17-data,
qui est monté sur /var/lib/odoo dans les conteneurs Odoo.

9.1 Créer le volume Docker

Le volume odoo17-data est défini dans docker-compose.yml mais n’est créé
que lorsqu’un conteneur Odoo démarre. Comme seul le conteneur db tourne à ce stade,
il faut créer le volume manuellement :

docker volume create odoo17-data

9.2 Extraire le filestore temporairement

tar -xzf /home/docker/odoo17/backup/odoo17-filestore.tar.gz -C /tmp/

Le dossier /tmp/filestore/ contient maintenant un sous-dossier par base de données (ex : /tmp/filestore/{{INSTANCE_1}}/).

9.3 Trouver le mountpoint du volume

docker volume inspect odoo17-data --format '{{ .Mountpoint }}'

Exemple de sortie : /var/lib/docker/volumes/odoo17-data/_data

9.4 Copier le filestore dans le volume

mkdir -p /var/lib/docker/volumes/odoo17-data/_data/filestore/
cp -r /tmp/filestore/* /var/lib/docker/volumes/odoo17-data/_data/filestore/

9.5 Corriger les permissions

Le conteneur Odoo s’exécute avec l’utilisateur UID 101 (utilisateur odoo dans le conteneur).
Il faut que les fichiers du filestore appartiennent à cet utilisateur :

chown -R 101:101 /var/lib/docker/volumes/odoo17-data/_data/filestore/

10) Démarrer toutes les instances Odoo

cd /home/docker/odoo17/
docker compose up -d

Vérifier l’état des conteneurs :

docker compose ps

Tous les conteneurs doivent être en état Up.

Consulter les logs en cas d’erreur :

docker compose logs odoo-{{INSTANCE_1}}
docker compose logs odoo-{{INSTANCE_2}}
docker compose logs odoo-test
docker compose logs odoo-admin

11) Tester localement (avant le DNS)

Depuis le serveur (en SSH), tester que chaque instance Odoo répond :

curl -I http://127.0.0.1:8069
curl -I http://127.0.0.1:8169
curl -I http://127.0.0.1:8269
curl -I http://127.0.0.1:8369

Chaque commande doit retourner HTTP/1.0 303 SEE OTHER (redirection vers /web).

À ce stade, les instances Odoo fonctionnent en local sur le serveur.
Elles ne sont pas encore accessibles depuis Internet tant que le DNS n’a pas été basculé.

12) Basculer les enregistrements DNS

Maintenant que tout fonctionne en local, on bascule les enregistrements A des domaines
pour qu’ils pointent vers le nouveau serveur.

Enregistrements DNS à mettre à jour

Chez ton registrar (OVH, Gandi, Cloudflare, etc.) ou dans la zone DNS du domaine :

Sous-domaine Type Cible
{{DOMAINE_ODOO_1}} A {{IP_NOUVEAU}}
{{DOMAINE_ODOO_2}} A {{IP_NOUVEAU}}
{{DOMAINE_ODOO_TEST}} A {{IP_NOUVEAU}}
{{DOMAINE_ODOO_ADMIN}} A {{IP_NOUVEAU}}
{{ALIAS_ODOO_1}} A {{IP_NOUVEAU}}

La propagation DNS prend généralement quelques minutes (si le TTL est faible, ex : 300 secondes),
mais peut aller jusqu’à 24 heures selon les FAI et les serveurs DNS récursifs.

Vérifier la propagation depuis ton PC :

nslookup {{DOMAINE_ODOO_1}}
dig +short {{DOMAINE_ODOO_1}}

Quand tu vois {{IP_NOUVEAU}}, le DNS a basculé.

13) Activer Let’s Encrypt (certificats SSL)

Une fois le DNS propagé, demander les certificats SSL pour chaque domaine :

/usr/local/vesta/bin/v-add-letsencrypt-domain {{USER_VESTA}} {{DOMAINE_ODOO_1}}
/usr/local/vesta/bin/v-add-letsencrypt-domain {{USER_VESTA}} {{DOMAINE_ODOO_2}}
/usr/local/vesta/bin/v-add-letsencrypt-domain {{USER_VESTA}} {{DOMAINE_ODOO_TEST}}
/usr/local/vesta/bin/v-add-letsencrypt-domain {{USER_VESTA}} {{DOMAINE_ODOO_ADMIN}}

Important : attendre que le DNS pointe bien vers le nouveau serveur avant de lancer ces commandes.
Let’s Encrypt valide le domaine via HTTP (challenge /.well-known/acme-challenge/).
Si le DNS pointe encore vers l’ancien serveur, la validation échouera.

MyVestaCP renouvelle automatiquement les certificats via un cron quotidien.
Pas besoin de configurer Certbot manuellement.

Future : Ajouter un nouveau module Odoo

Pour installer un nouveau module Odoo personnalisé (téléchargé depuis GitHub, Odoo Apps Store, ou développé en interne) :

Étape 1 : Uploader le module via FileZilla

  1. Ouvrir FileZilla et se connecter au serveur (même connexion SFTP que dans la section 4.1)
  2. Naviguer vers /home/docker/odoo17/ext_addons/ (panneau de droite)
  3. Glisser-déposer le dossier du module depuis ton PC (panneau de gauche)

Exemple : tu uploades my_custom_module/, qui contient __manifest__.py, models/, views/, etc.

Étape 2 : Redémarrer l’instance Odoo

Depuis le serveur (SSH) :

cd /home/docker/odoo17/
docker compose restart odoo-{{INSTANCE_1}}

Ou pour redémarrer toutes les instances :

docker compose restart

Étape 3 : Activer le mode développeur dans Odoo

Se connecter à l’instance Odoo → Paramètres → tout en bas → Activer le mode développeur.

Étape 4 : Mettre à jour la liste des applications

Applications → menu hamburger (trois points) → Mettre à jour la liste des applications.

Étape 5 : Installer le module

Chercher le nom du module dans la liste → cliquer sur Installer.

Grâce au volume ./ext_addons:/mnt/extra-addons dans docker-compose.yml,
tous les modules uploadés dans /home/docker/odoo17/ext_addons/ sont immédiatement disponibles
dans toutes les instances Odoo ({{INSTANCE_1}}, {{INSTANCE_2}}, test, admin).

Récapitulatif de l’architecture finale

Voici l’architecture complète après migration :

MyVestaCP (proxy + SSL)                Docker (application)
─────────────────────────               ─────────────────────
/home/{{USER_VESTA}}/web/              /home/docker/odoo17/
├── {{DOMAINE_ODOO_1}}/                ├── docker-compose.yml
│   └── Nginx → 127.0.0.1:8069        ├── config/
├── {{DOMAINE_ODOO_2}}/                │   ├── odoo-{{INSTANCE_1}}.conf
│   └── Nginx → 127.0.0.1:8169        │   ├── odoo-{{INSTANCE_2}}.conf
├── {{DOMAINE_ODOO_TEST}}/             │   ├── odoo-test.conf
│   └── Nginx → 127.0.0.1:8269        │   └── odoo-admin.conf
└── {{DOMAINE_ODOO_ADMIN}}/            ├── ext_addons/
    └── Nginx → 127.0.0.1:8369        │   ├── module1/
                                        │   ├── module2/
                                        │   └── ... (autres modules)
                                        └── backup/

Docker Compose services:
  - odoo17-db (PostgreSQL 13, partagée)
  - odoo17-{{INSTANCE_1}} (port 8069/8072)
  - odoo17-{{INSTANCE_2}} (port 8169/8172)
  - odoo17-test (port 8269/8272)
  - odoo17-admin (port 8369/8372)

Points clés de cette architecture :

  • Séparation des responsabilités : MyVestaCP gère le proxy et le SSL, Docker gère l’application
  • Isolation : chaque instance Odoo a sa propre configuration (dbfilter, workers)
  • Sécurité : les ports Odoo sont bindés sur 127.0.0.1 (pas d’accès direct depuis Internet)
  • Scalabilité : facile d’ajouter une 5ème instance en dupliquant un service dans docker-compose.yml
  • Maintenance : les modules custom sont centralisés dans ext_addons/ et partagés par toutes les instances

Configuration post-installation

Définir le type de produit par défaut sur « Produit stockable »

Par défaut, Odoo 17 crée tous les nouveaux produits en Consommable.
Pour éviter de devoir changer manuellement le type à chaque création de produit,
il faut définir Produit stockable comme valeur par défaut.

Important : sans ce réglage, les nouveaux produits ne génèrent pas de mouvements de stock
et l’inventaire ne sera pas suivi correctement.
  1. Activer le mode développeur :
    Paramètres → Général → tout en bas → Activer le mode développeur
    (ou ajouter ?debug=1 à l’URL)
  2. Aller dans Inventaire → Produits → Créer
  3. Changer le champ Type de produit en Produit stockable
  4. Ne pas sauvegarder le produit
  5. Faire un clic droit sur le champ « Type de produit » →
    Définir comme valeur par défaut
  6. Choisir Pour tous les utilisateurs pour que ce soit appliqué globalement
  7. Valider, puis supprimer le brouillon de produit
Ce réglage est stocké dans ir.default et persiste après les mises à jour.
Il s’applique à tous les nouveaux produits créés après la modification.

À suivre

Odoo 17 est maintenant opérationnel en Docker avec reverse proxy et SSL.
Dans les prochains articles :

  • Odoo 8 en Docker — migration séparée (Python 2.7, PostgreSQL 9.6, ancien format de modules)
  • Akeneo PIM — gestion centralisée des produits (Docker, MySQL, Elasticsearch)
  • ownCloud — migration des données et Docker Compose
  • Serveurs de jeux — CS 1.6, TeamSpeak 3, EmuLinker
  • Sauvegardes — stratégie et automatisation
Article précédent : Migration des sites web — WordPress, PrestaShop, sites statiques.

 

Catégories : Non classé

0 commentaire

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *