[UPDATE 2026] Akeneo PIM 6 vers 7 en Docker — migration, upgrade et centralisation serveur avec MyVestaCP

Published by David on

Akeneo PIM 6 → 7 en Docker — migration et upgrade

Sixième étape : migrer Akeneo PIM 6 (Community Edition) d’un VPS standalone
vers PIM 7 en Docker Compose sur le serveur centralisé, avec reverse proxy MyVestaCP.

Paramètres à adapter

Ce document utilise des variables entre {{...}}. Remplacez-les par vos propres valeurs avant d’exécuter les commandes.

Variable Description Exemple
{{HOSTNAME_VPS_PIM}} Hostname du VPS PIM (ancien) vps-pim-ancien
{{IP_VPS_PIM}} IP du VPS PIM 203.0.113.50
{{USER_VPS_PIM}} Utilisateur SSH du VPS PIM ubuntu
{{HOSTNAME_NOUVEAU}} Hostname du nouveau serveur vps-nouveau
{{IP_NOUVEAU}} IP du nouveau serveur 198.51.100.20
{{PORT_SSH_NOUVEAU}} Port SSH du nouveau serveur 22
{{DOMAINE_PIM}} Domaine Akeneo PIM pim.exemple.com
{{MYSQL_ROOT_PASSWORD}} Mot de passe root MySQL MonMotDePasseRoot!
{{APP_SECRET}} Secret Symfony (APP_SECRET) GenerateWithOpenSSLRand
{{USER_VESTA}} Utilisateur VestaCP admin

Les identifiants MySQL akeneo_pim (user/db/password) sont les valeurs par défaut Akeneo. Changez le mot de passe en production.

Introduction

Akeneo PIM 6 (Community Edition, v6.0.45) tourne actuellement sur un VPS séparé
({{HOSTNAME_VPS_PIM}}, IP {{IP_VPS_PIM}}, SSH port 22, utilisateur SSH {{USER_VPS_PIM}}).
L’installation est en standalone : PHP 8.0, MySQL 8.0.30, Elasticsearch 7.x, le tout installé
directement sur le système dans /home/pim-community-standard/.

On migre vers le serveur centralisé ({{HOSTNAME_NOUVEAU}}, IP {{IP_NOUVEAU}},
SSH port {{PORT_SSH_NOUVEAU}}) en Docker Compose, avec un upgrade de PIM 6.0 vers PIM 7.0
en même temps.

Architecture cible

  • 6 conteneurs Docker : MySQL 8.0.30, Elasticsearch 8.4.2, PHP 8.1 FPM, Nginx, Job Worker, Cron
  • Domaine : {{DOMAINE_PIM}} (port interne 8480)
  • MyVestaCP gère le reverse proxy et les certificats SSL
  • Fichiers Docker : /home/docker/akeneo/

Ce qui change entre PIM 6 et PIM 7 :
PHP 8.0 → 8.1, Elasticsearch 7.x → 8.4.2,
le SKU n’est plus obligatoire (UUID devient l’identifiant technique),
les nouveaux endpoints API utilisent /products-uuid,
et la configuration du mailer passe en format DSN (MAILER_URLMAILER_DSN).

Ressources nécessaires : Elasticsearch 8 consomme ~2-4 Go de RAM,
MySQL ~1-2 Go, PHP-FPM ~1 Go. Assurez-vous que la RAM disponible sur votre serveur est suffisant pour votre usage,
même en coexistence avec d’autres services.

1) Créer le domaine dans MyVestaCP

Dans le panel MyVestaCP → WEBAdd Web Domain :

  • {{DOMAINE_PIM}}

Paramètres :

  • DNS Support : cocher (obligatoire pour Let’s Encrypt)
  • Mail Support : décocher
  • Advanced options : laisser les valeurs par défaut

DNS Support obligatoire : même si le DNS est géré ailleurs (OVH, Cloudflare, etc.),
il faut cocher DNS Support dans MyVestaCP. Le script v-add-letsencrypt-domain
a besoin de la zone DNS locale pour créer l’enregistrement _acme-challenge.
Sans cette zone, la demande de certificat échoue avec une erreur 404.

Important : ne pas activer Let’s Encrypt pour l’instant — le DNS ne pointe pas encore vers le nouveau serveur.

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

Même principe que pour Odoo : on crée des templates Nginx personnalisés pour que VestaCP
fasse du reverse proxy vers le conteneur Docker (port 8480).

2.1 Template HTTP : akeneo-8480.tpl

nano /usr/local/vesta/data/templates/web/nginx/akeneo-8480.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 / {
        proxy_pass http://127.0.0.1:8480;
        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;
        client_max_body_size 128M;
    }

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

Le client_max_body_size 128M est important pour PIM : l’import de fichiers CSV
et l’upload d’images produits peuvent être volumineux.

2.2 Template HTTPS : akeneo-8480.stpl

nano /usr/local/vesta/data/templates/web/nginx/akeneo-8480.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 / {
        proxy_pass http://127.0.0.1:8480;
        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 https;
        proxy_set_header X-Forwarded-Host $host;
        proxy_read_timeout 720s;
        proxy_connect_timeout 720s;
        proxy_send_timeout 720s;
        client_max_body_size 128M;
    }

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

2.3 Appliquer le template au domaine

/usr/local/vesta/bin/v-change-web-domain-proxy-tpl {{USER_VESTA}} {{DOMAINE_PIM}} akeneo-8480

2.4 Vérifier et recharger Nginx

nginx -t
systemctl reload nginx

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

mkdir -p /home/docker/akeneo/pim
mkdir -p /home/docker/akeneo/config/nginx
mkdir -p /home/docker/akeneo/backup
  • pim/ : code source Akeneo PIM 7 (installé via Composer)
  • config/nginx/ : configuration Nginx pour le conteneur interne
  • config/crontab : tâches planifiées PIM (créé en section 4.3)
  • backup/ : exports temporaires (dump MySQL, tar filestore)

4) Créer les fichiers de configuration Docker

4.1 Créer le docker-compose.yml

nano /home/docker/akeneo/docker-compose.yml

Contenu :

services:
  mysql:
    image: mysql:8.0.30
    container_name: akeneo-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: {{MYSQL_ROOT_PASSWORD}}
      MYSQL_DATABASE: akeneo_pim
      MYSQL_USER: akeneo_pim
      MYSQL_PASSWORD: akeneo_pim
    volumes:
      - akeneo-mysql-data:/var/lib/mysql
    command: ['--default-authentication-plugin=mysql_native_password', '--log-bin-trust-function-creators=1']
    networks:
      - akeneo-network

  elasticsearch:
    image: elastic/elasticsearch:8.4.2
    container_name: akeneo-es
    restart: always
    environment:
      ES_JAVA_OPTS: '-Xms512m -Xmx512m'
      discovery.type: single-node
      xpack.security.enabled: 'false'
      xpack.security.http.ssl.enabled: 'false'
    volumes:
      - akeneo-es-data:/usr/share/elasticsearch/data
    networks:
      - akeneo-network

  php:
    image: akeneo/pim-php-dev:8.1
    container_name: akeneo-php
    restart: always
    user: "0:0"
    volumes:
      - ./pim:/srv/pim
    working_dir: /srv/pim
    depends_on:
      - mysql
      - elasticsearch
    environment:
      APP_ENV: prod
      XDEBUG_MODE: 'off'
      PHP_MEMORY_LIMIT: 1024M
    networks:
      - akeneo-network

  nginx:
    image: nginx:1.25-alpine
    container_name: akeneo-nginx
    restart: always
    ports:
      - "127.0.0.1:8480:80"
    volumes:
      - ./pim/public:/srv/pim/public:ro
      - ./config/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php
    networks:
      - akeneo-network

  job-worker:
    image: akeneo/pim-php-dev:8.1
    container_name: akeneo-job
    restart: always
    user: www-data
    volumes:
      - ./pim:/srv/pim
    working_dir: /srv/pim
    command: ['php', 'bin/console', 'messenger:consume', 'ui_job', 'import_export_job', 'data_maintenance_job', 'scheduled_job', '--time-limit=300', '--memory-limit=1024M']
    depends_on:
      - php
      - mysql
      - elasticsearch
    environment:
      APP_ENV: prod
      XDEBUG_MODE: 'off'
    networks:
      - akeneo-network

  cron:
    image: docker:27-cli
    container_name: akeneo-cron
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config/crontab:/crontab:ro
    entrypoint: ["/bin/sh", "-c", "cp /crontab /var/spool/cron/crontabs/root && chmod 0600 /var/spool/cron/crontabs/root && crond -f"]
    depends_on:
      - php

volumes:
  akeneo-mysql-data:
    name: akeneo-mysql-data
  akeneo-es-data:
    name: akeneo-es-data

networks:
  akeneo-network:
    name: akeneo-network

Points clés :

  • mysql:8.0.30 avec mysql_native_password (Akeneo ne supporte pas le nouveau plugin d’authentification MySQL 8) et log_bin_trust_function_creators=1 (nécessaire pour les migrations qui créent des triggers)
  • elastic/elasticsearch:8.4.2 en mode single-node, sécurité désactivée (réseau Docker interne)
  • akeneo/pim-php-dev:8.1 pour PHP-FPM avec toutes les extensions requises. Le user: "0:0" fait tourner le conteneur en root pour éviter les erreurs de permission sur les logs PHP-FPM et le cache Symfony
  • nginx:1.25-alpine comme serveur web interne, exposé sur le port 8480
  • job-worker : démon qui traite les imports/exports et tâches asynchrones via 4 queues PIM 7 (ui_job, import_export_job, data_maintenance_job, scheduled_job). Le --time-limit=300 le redémarre toutes les 5 minutes (libère la mémoire)
  • cron : conteneur léger (docker:27-cli, ~15 Mo) qui exécute les tâches planifiées PIM via docker exec dans le conteneur PHP. Tout est dans le compose, pas de crontab à configurer sur l’hôte
  • Réseau et volumes complètement séparés d’Odoo

Image akeneo/pim-php-dev:8.1 : c’est l’image officielle Akeneo pour le développement.
Elle inclut toutes les extensions PHP requises (apcu, bcmath, imagick, intl, etc.). Pour la production,
Akeneo ne fournit pas d’image officielle. On désactive Xdebug via XDEBUG_MODE=off.

user: "0:0" sur le service php : sans cela, le conteneur tourne en www-data
et PHP-FPM ne peut pas écrire son fichier de log (Permission denied: /var/log/php8.1-fpm.log),
ce qui provoque un crash en boucle de supervisord. Le job-worker garde user: www-data
car il n’a pas besoin d’accès root (il exécute uniquement des commandes Symfony).

Queues PIM 7 : PIM 6 utilisait une seule queue (main_queue).
PIM 7 la remplace par 4 queues spécialisées : ui_job, import_export_job,
data_maintenance_job, scheduled_job. Si vous gardez main_queue,
le job-worker crashe avec No such transport "main_queue".

Conteneur cron et Docker socket : le service cron monte le
docker.sock en lecture seule pour pouvoir exécuter docker exec
dans le conteneur PHP. C’est le même mécanisme que la crontab hôte, mais conteneurisé.
Les commandes PIM tournent dans le contexte du conteneur akeneo-php
(mêmes volumes, même réseau, mêmes variables d’environnement).
Avantage : un docker compose down && up -d restaure tout automatiquement,
pas de crontab à reconfigurer manuellement.

4.2 Créer la configuration Nginx interne

nano /home/docker/akeneo/config/nginx/default.conf

Contenu :

server {
    listen 80;
    server_name _;
    root /srv/pim/public;
    client_max_body_size 128M;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass php:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_read_timeout 600;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        internal;
    }

    location ~ \.php$ {
        return 404;
    }
}

Ce Nginx interne (dans Docker) proxie les requêtes PHP vers le conteneur php (FPM port 9000).
Le Nginx de MyVestaCP (sur l’hôte) proxie vers ce Nginx interne sur le port 8480.
C’est une architecture classique « double reverse proxy ».

4.3 Créer le fichier crontab

nano /home/docker/akeneo/config/crontab

Contenu :

# Akeneo PIM 7 - Crons conteneurisés (documentation officielle CE)
# Les commandes s'exécutent dans le conteneur akeneo-php via docker exec
# Source : https://docs.akeneo.com/7.0/install_pim/manual/installation_ce.html

# === DATA QUALITY INSIGHTS (DQI) ===

# Planifier les tâches DQI périodiques (tous les jours à 00:15)
15 0 * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:data-quality-insights:schedule-periodic-tasks --env=prod 2>&1

# Préparer les évaluations DQI (toutes les 10 minutes)
*/10 * * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:data-quality-insights:prepare-evaluations --env=prod 2>&1

# Exécuter les évaluations DQI (toutes les 30 minutes)
*/30 * * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:data-quality-insights:evaluations --env=prod 2>&1

# Nettoyer les résultats de complétude DQI (tous les jours à 00:30)
30 0 * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:data-quality-insights:clean-completeness-evaluation-results --no-interaction --env=prod 2>&1

# === VERSIONING ===

# Rafraîchir les versions (tous les jours à 03:00)
0 3 * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:versioning:refresh --env=prod 2>&1

# Purger les anciennes versions (dimanche à 01:00)
0 1 * * 0 docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:versioning:purge --more-than-days 90 --no-interaction --force --env=prod 2>&1

# === MAINTENANCE ===

# Purger les anciens jobs (1er du mois à minuit)
0 0 1 * * docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:batch:purge-job-execution --env=prod 2>&1

# Agréger les volumes (tous les jours à 04:00)
0 4 * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:volume:aggregate --env=prod 2>&1

# Pousser les jobs planifiés dans la queue (toutes les 10 minutes)
*/10 * * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console pim:job-automation:push-scheduled-jobs-to-queue --env=prod 2>&1

# Purger les messages Doctrine (toutes les 2 heures)
0 */2 * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:messenger:doctrine:purge-messages messenger_messages default --env=prod 2>&1

# === CONNECTIVITY ===

# Audit de connectivité (toutes les heures à :01)
1 * * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:connectivity-audit:update-data --env=prod 2>&1

# Purger les erreurs de connexion (toutes les heures à :10)
10 * * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:connectivity-connection:purge-error --env=prod 2>&1

# Purger le compteur d'erreurs d'audit (tous les jours à 12:40)
40 12 * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:connectivity-audit:purge-error-count --env=prod 2>&1

# Purger les logs Events API (toutes les heures à :05)
5 * * * * docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:connectivity-connection:purge-events-api-logs --env=prod 2>&1

# Rotation des clés OpenID (dimanche à 21:04)
4 21 * * 0 docker exec -u www-data akeneo-php php /srv/pim/bin/console akeneo:connectivity-connection:openid-keys:create --no-interaction --env=prod 2>&1

Ligne vide obligatoire à la fin : BusyBox crond (utilisé par Alpine)
exige une ligne vide à la fin du fichier crontab. Sans elle, la dernière entrée est ignorée
silencieusement.

15 crons officiels CE : ces crons sont regroupés en 4 catégories :
Data Quality Insights (4 crons — calcul des scores qualité),
Versioning (2 crons — historique des modifications),
Maintenance (4 crons — purge, volumes, jobs planifiés, messages),
Connectivity (5 crons — audit, purge erreurs, logs API, clés OpenID).
Source : documentation officielle PIM 7.0 CE.

Modifier les crons : pour ajouter ou supprimer un cron, éditer le fichier
/home/docker/akeneo/config/crontab puis redémarrer le conteneur :
docker compose restart cron.

5) Installer le code PIM 7 via Composer

On utilise l’image Docker PHP pour télécharger et installer PIM 7 sans rien installer sur l’hôte.

# Installer PIM 7 Community Edition via Composer
# On monte le PARENT (/home/docker/akeneo) et on laisse Composer créer le sous-répertoire pim
docker run -ti --rm \
  -v /home/docker/akeneo:/srv/akeneo \
  -w /srv/akeneo \
  akeneo/pim-php-dev:8.1 \
  php /usr/local/bin/composer create-project \
    akeneo/pim-community-standard /srv/akeneo/pim "7.0.*@stable"

Point de montage : ne pas monter directement /home/docker/akeneo/pim
comme volume — Composer a besoin de créer le répertoire cible lui-même.
On monte donc le répertoire parent et on laisse create-project
créer le sous-répertoire pim. La commande tourne en root
(pas -u www-data) pour éviter les erreurs de permission.

5.0.1 Corriger les permissions après installation

# Remettre la propriété à www-data (UID 33) pour les conteneurs PHP/Nginx
chown -R 33:33 /home/docker/akeneo/pim

5.0.2 Créer le fichier docker/supervisord.conf

L’image akeneo/pim-php-dev:8.1 utilise supervisord comme processus principal,
mais pim-community-standard installé via Composer n’inclut pas le fichier de configuration.
Sans ce fichier, le conteneur PHP crashe en boucle avec Error: could not find config file docker/supervisord.conf.

mkdir -p /home/docker/akeneo/pim/docker

cat > /home/docker/akeneo/pim/docker/supervisord.conf << 'EOF'
[supervisord]
nodaemon=true
user=root
logfile=/dev/null
logfile_maxbytes=0

[program:php-fpm]
command=/usr/local/sbin/php-fpm -F
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
EOF

chown -R 33:33 /home/docker/akeneo/pim/docker/

user=root et -F sont obligatoires :
sans user=root, supervisord affiche un warning CRIT running as root, privileges were not dropped.
Sans -F (foreground), PHP-FPM se détache en arrière-plan (daemonize) : le processus parent
quitte avec status 0, supervisord croit qu’il a crashé et tente de le relancer, mais le port 9000
est toujours occupé par le processus enfant orphelin — résultat : crash en boucle avec
Address already in use (98).

Première exécution longue : Composer télécharge toutes les dépendances (~500 Mo).
Cette commande peut prendre 5-10 minutes selon la connexion.
Si elle échoue sur un timeout, relancer avec COMPOSER_PROCESS_TIMEOUT=600.

5.1 Créer le fichier .env.local

Ce fichier contient la configuration spécifique à notre environnement Docker.
Il surcharge les valeurs par défaut de .env.

nano /home/docker/akeneo/pim/.env.local

Contenu :

APP_ENV=prod
APP_SECRET={{APP_SECRET}}

APP_DATABASE_HOST=mysql
APP_DATABASE_PORT=3306
APP_DATABASE_NAME=akeneo_pim
APP_DATABASE_USER=akeneo_pim
APP_DATABASE_PASSWORD=akeneo_pim

APP_INDEX_HOSTS=elasticsearch:9200
APP_PRODUCT_AND_PRODUCT_MODEL_INDEX_NAME=akeneo_pim_product_and_product_model
APP_CONNECTION_ERROR_INDEX_NAME=akeneo_connectivity_connection_error
APP_EVENTS_API_DEBUG_INDEX_NAME=akeneo_connectivity_connection_events_api_debug
APP_ELASTICSEARCH_TOTAL_FIELDS_LIMIT=10000

AKENEO_PIM_URL=https://{{DOMAINE_PIM}}

MAILER_URL=null://localhost

APP_DEFAULT_LOCALE=en
APP_DEBUG=0
XDEBUG_MODE=off

Différences avec PIM 6 :
APP_DATABASE_HOST pointe vers mysql (nom du service Docker, pas localhost).
APP_INDEX_HOSTS pointe vers elasticsearch:9200.
AKENEO_PIM_URL utilise https:// (après activation Let’s Encrypt).

5.2 Permissions

# Le code PIM doit appartenir à www-data (UID 33)
sudo chown -R 33:33 /home/docker/akeneo/pim/

# docker-compose.yml : lecture root uniquement
sudo chmod 600 /home/docker/akeneo/docker-compose.yml

# Répertoires Docker : traversables
sudo chmod 755 /home/docker/akeneo/
sudo chmod 755 /home/docker/akeneo/config/
sudo chmod 755 /home/docker/akeneo/config/nginx/

6) Préparer la base PIM 6 avant l’export

Avant d’exporter la base, il faut s’assurer que toutes les migrations PIM 6
ont été appliquées. On fait cela sur le VPS PIM 6.

# Sur le VPS PIM (root@{{HOSTNAME_VPS_PIM}})
cd /home/pim-community-standard/

# Copier les fichiers de migration
cp -R ./vendor/akeneo/pim-community-dev/upgrades/* ./upgrades/

# Vider le cache (en tant que root)
rm -rf var/cache/*

# Appliquer toutes les migrations PIM 6 en attente
sudo -u www-data php bin/console doctrine:migrations:migrate --no-interaction

# Reconstruire le cache (obligatoire après le nettoyage)
sudo -u www-data php bin/console cache:clear --env=prod
sudo -u www-data php bin/console cache:warmup --env=prod

Permissions : le rm -rf var/cache/* doit être fait en root,
mais les commandes cache:clear et cache:warmup doivent être lancées
en tant que www-data (via sudo -u www-data), sinon le cache sera recréé
avec des permissions root et PIM 6 retournera une erreur 500 Internal Server Error au prochain redémarrage.
Si cela arrive, corriger avec : chown -R www-data:www-data /home/pim-community-standard/var/

Si doctrine:migrations:migrate affiche No migrations to execute,
c’est normal — cela signifie que la base PIM 6 est déjà à jour.

7) Exporter les données depuis le VPS PIM 6

Sur le VPS PIM ({{IP_VPS_PIM}}), on exporte la base MySQL et le filestore.

7.1 Arrêter les workers PIM 6

# Sur le VPS PIM ({{USER_VPS_PIM}}@{{HOSTNAME_VPS_PIM}})
# Arrêter les démons de queue pour éviter les modifications pendant l'export
sudo supervisorctl stop all 2>/dev/null
# Ou si pas de supervisord, trouver et arrêter les process PHP daemon
ps aux | grep "messenger:consume\|job-queue" | grep -v grep

7.2 Exporter la base MySQL

# Sur le VPS PIM
mysqldump -u akeneo_pim -pakeneo_pim akeneo_pim | gzip > /home/{{USER_VPS_PIM}}/akeneo-pim-db.sql.gz

La base se nomme akeneo_pim, utilisateur akeneo_pim,
mot de passe akeneo_pim (tel que configuré dans le .env de PIM 6).

7.3 Exporter le filestore

# Sur le VPS PIM
cd /home/pim-community-standard/
tar -czf /home/{{USER_VPS_PIM}}/akeneo-pim-filestore.tar.gz var/file_storage/

7.4 Exporter le cache média (optionnel)

# Le cache média sera régénéré automatiquement, mais pour éviter d'attendre :
cd /home/pim-community-standard/
tar -czf /home/{{USER_VPS_PIM}}/akeneo-pim-media.tar.gz public/media/ 2>/dev/null

8) Transférer les exports via rsync

Depuis le nouveau serveur ({{IP_NOUVEAU}}), récupérer les archives :

# Depuis le NOUVEAU serveur
rsync -avz -e "ssh" \
  {{USER_VPS_PIM}}@{{IP_VPS_PIM}}:/home/{{USER_VPS_PIM}}/akeneo-pim-db.sql.gz \
  /home/docker/akeneo/backup/

rsync -avz -e "ssh" \
  {{USER_VPS_PIM}}@{{IP_VPS_PIM}}:/home/{{USER_VPS_PIM}}/akeneo-pim-filestore.tar.gz \
  /home/docker/akeneo/backup/

rsync -avz -e "ssh" \
  {{USER_VPS_PIM}}@{{IP_VPS_PIM}}:/home/{{USER_VPS_PIM}}/akeneo-pim-media.tar.gz \
  /home/docker/akeneo/backup/ 2>/dev/null

Le VPS PIM utilise le port SSH par défaut (22), donc pas besoin de -p.
Si une clé SSH n’est pas configurée, utiliser scp à la place.

9) Démarrer les services et restaurer la base

9.1 Démarrer MySQL et Elasticsearch

cd /home/docker/akeneo/
docker compose up -d mysql elasticsearch

Ne pas démarrer tous les services : à cette étape, ne lancer que
mysql et elasticsearch. Le conteneur PHP sera démarré après
la restauration de la base et la correction des permissions (sections 9.2 et 10).

Attendre que MySQL soit prêt :

# Attendre ~15 secondes puis vérifier
docker compose exec mysql mysqladmin ping -u akeneo_pim -pakeneo_pim

Quand tu vois mysqld is alive, c’est prêt.

Vérifier Elasticsearch :

docker compose exec elasticsearch curl -s http://localhost:9200/_cluster/health | python3 -m json.tool

Le status doit être green ou yellow.

9.2 Supprimer la base vide et restaurer le dump PIM 6

# Supprimer la base créée automatiquement par Docker
docker compose exec mysql mysql -u root -p{{MYSQL_ROOT_PASSWORD}} \
  -e "DROP DATABASE akeneo_pim; CREATE DATABASE akeneo_pim DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# Restaurer le dump PIM 6
gunzip -c /home/docker/akeneo/backup/akeneo-pim-db.sql.gz | \
  docker compose exec -T mysql mysql -u akeneo_pim -pakeneo_pim akeneo_pim

Attention : la restauration peut prendre plusieurs minutes selon la taille de la base.
Ne pas interrompre le processus.

9.3 Restaurer le filestore

# Extraire le filestore dans le répertoire PIM
tar -xzf /home/docker/akeneo/backup/akeneo-pim-filestore.tar.gz \
  -C /home/docker/akeneo/pim/

# Restaurer le cache média (si exporté)
tar -xzf /home/docker/akeneo/backup/akeneo-pim-media.tar.gz \
  -C /home/docker/akeneo/pim/ 2>/dev/null

# Corriger les permissions
sudo chown -R 33:33 /home/docker/akeneo/pim/var/
sudo chown -R 33:33 /home/docker/akeneo/pim/public/media/ 2>/dev/null

10) Exécuter la migration PIM 6.0 → 7.0

Maintenant que la base PIM 6 est restaurée dans le conteneur MySQL et que le code PIM 7
est installé, on peut exécuter les migrations Doctrine pour upgrader le schéma.

10.0.1 Corriger les permissions du cache

Le code PIM a été installé en root (section 5), donc le répertoire var/
appartient à root. Il faut corriger les permissions depuis l’intérieur du conteneur
pour que www-data puisse créer le cache.

# Créer et corriger les permissions du cache (depuis l'intérieur du conteneur)
docker compose exec -u root php bash -c "mkdir -p /srv/pim/var/cache/prod /srv/pim/var/log && chown -R www-data:www-data /srv/pim/var/"

Important : la commande chown doit être faite depuis l’intérieur
du conteneur
(via docker compose exec), pas depuis l’hôte. Le chown
depuis l’hôte avec chown -R 33:33 peut ne pas fonctionner correctement selon le type
de volume Docker utilisé.

10.1 Démarrer le conteneur PHP

cd /home/docker/akeneo/
docker compose up -d php

10.2 Vider le cache

docker compose exec -u www-data php rm -rf var/cache/

10.3 Créer les index Elasticsearch (obligatoire avant les migrations)

Les migrations Doctrine incluent une étape de conversion UUID qui a besoin des index Elasticsearch.
Si les index n’existent pas, la migration échouera avec index_not_found_exception
sur akeneo_pim_product_and_product_model.

docker compose exec -u www-data php php bin/console akeneo:elasticsearch:reset-indexes --no-interaction

10.4 Exécuter les migrations Doctrine

docker compose exec -u www-data php php bin/console doctrine:migrations:migrate --no-interaction

Migration très longue (1 à 3 heures) : cette commande applique toutes les migrations de schéma
entre PIM 5.0 et PIM 7.0 (les versions 5.0 → 6.0 sont incluses dans le code PIM 7).
Les étapes les plus longues sont :

  • ALTER TABLE pim_versioning_version — cette table stocke l’historique complet
    de chaque modification de chaque entité PIM. C’est la plus grosse table de la base (souvent > 1 Go).
    L’ALTER TABLE crée une copie temporaire, ce qui peut prendre 1 à 2 heures.
  • Conversion UUID (Version_7_0_20220429131804_execute_uuid_migration) —
    crée des triggers temporaires sur plusieurs tables pour migrer les identifiants vers des UUID.
  • INSERT INTO pim_data_quality_insights_*
    peuple les tables de scoring qualité, plusieurs milliers de lignes par table.

Monitoring : pour vérifier que la migration progresse, ouvrir un second terminal et observer le processlist MySQL :
docker compose exec mysql mysql -u akeneo_pim -pakeneo_pim akeneo_pim -e "SHOW FULL PROCESSLIST;".
Un Time qui augmente sur un ALTER TABLE est normal.
Des warnings did not result in any SQL statements sont bénins.

10.5 Migrations de données complémentaires

# Mettre à jour les scores de qualité des données
docker compose exec -u www-data php php bin/console \
  pim:data-quality-insights:populate-product-models-scores-and-ki

11) Réindexer Elasticsearch et compiler les assets

11.1 Réindexer Elasticsearch

# Réinitialiser les index (PIM 7 utilise ES 8.4.2, structure différente)
docker compose exec -u www-data php php bin/console akeneo:elasticsearch:reset-indexes

# Réindexer tous les produits
docker compose exec -u www-data php php bin/console pim:product:index --all

# Réindexer les modèles de produits
docker compose exec -u www-data php php bin/console pim:product-model:index --all

La réindexation peut prendre du temps si tu as beaucoup de produits.
C’est normal — Elasticsearch reconstruit tout l’index depuis MySQL.

11.2 Préparer les fichiers requis par webpack

Webpack a besoin de fichiers générés par Symfony (routes JS, require-paths, bundles publics).
Ces commandes doivent être exécutées depuis le conteneur PHP avant de lancer Node.

# Créer les répertoires nécessaires
docker compose exec -u root php bash -c "mkdir -p /srv/pim/public/js && chown www-data:www-data /srv/pim/public/js"

# Générer require-paths.js
docker compose exec -u www-data php php bin/console pim:installer:dump-require-paths

# Générer les routes JavaScript
docker compose exec -u www-data php php bin/console fos:js-routing:dump --format=json --target=public/js/fos_js_routes.json

# Installer les assets des bundles Symfony (copie, pas symlink)
docker compose exec -u root php php bin/console assets:install public

assets:install sans --symlink : utiliser la copie (pas les liens symboliques).
Avec --symlink, webpack suit les symlinks vers vendor/ et ne trouve plus
les modules @akeneo-pim-community/* dans node_modules/.
La commande assets:install doit tourner en root pour pouvoir créer
les répertoires dans public/bundles/.

11.3 Compiler les assets frontend

La compilation se fait avec un conteneur node:18 éphémère.
L’ordre des commandes est important : packages:build (compile les front-packages
TypeScript en JavaScript), puis less, puis webpack.

# 1. Installer les dépendances et builder les workspace packages
docker run -ti --rm -u root \
  -v /home/docker/akeneo/pim:/srv/pim \
  -w /srv/pim \
  node:18 yarn install

# 2. Compiler les front-packages TypeScript (génère lib/index.js)
docker run -ti --rm -u root \
  -v /home/docker/akeneo/pim:/srv/pim \
  -w /srv/pim \
  node:18 yarn run packages:build

# 3. Compiler les feuilles de style LESS
docker run -ti --rm -u root \
  -v /home/docker/akeneo/pim:/srv/pim \
  -w /srv/pim \
  node:18 yarn run less

# 4. Compiler le bundle JavaScript final
docker run -ti --rm -u root \
  -v /home/docker/akeneo/pim:/srv/pim \
  -w /srv/pim \
  node:18 yarn run webpack

packages:build obligatoire : sans cette étape, webpack échoue avec 333 erreurs
Module not found: Can't resolve '@akeneo-pim-community/shared'.
Les front-packages sont en TypeScript source (src/) et leur package.json
déclare "main": "./lib/index.js". Le répertoire lib/ n’existe qu’après
la compilation par packages:build (qui lance tsc sur chaque workspace).

Compilation longue : le build complet (install + packages + less + webpack)
prend environ 5-10 minutes et consomme beaucoup de RAM.
C’est une opération unique (pas besoin de le refaire à chaque redémarrage).
Les commandes tournent en -u root pour éviter les erreurs de permission
sur node_modules/.

11.4 Générer extensions.json

Le frontend PIM charge /js/extensions.json au démarrage pour connaître la liste
des extensions de formulaires (menus, onglets, champs, etc.). Sans ce fichier, le dashboard
reste bloqué sur « Loading… » à l’infini après la connexion.

Ce fichier est généré par un script Node (pas par une commande Symfony) qui lit
require-paths.js (généré en 11.2), scanne les fichiers form_extensions.yml
de chaque bundle, et les fusionne en un seul JSON.

# Générer extensions.json (obligatoire pour que le frontend fonctionne)
docker run -ti --rm -u root \
  -v /home/docker/akeneo/pim:/srv/pim \
  -w /srv/pim \
  node:18 node vendor/akeneo/pim-community-dev/frontend/build/update-extensions.js

Fichier critique : aucune commande bin/console ne génère ce fichier.
Il n’est référencé dans aucune documentation officielle Akeneo pour les installations self-hosted.
Si /js/extensions.json retourne 404, le PIM se connecte mais affiche un écran
« Loading… » infini avec l’animation du dinosaure.

11.5 Corriger les permissions après compilation

# Les assets ont été créés en root, remettre www-data pour PHP/Nginx
chown -R 33:33 /home/docker/akeneo/pim/public/

11.6 Vider le cache Symfony

docker compose exec -u www-data php php bin/console cache:clear --env=prod

12) Démarrer tous les services

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

Vérifier l’état de tous les conteneurs :

docker compose ps

Les 6 conteneurs doivent être en état Up :

  • akeneo-mysql
  • akeneo-es
  • akeneo-php
  • akeneo-nginx
  • akeneo-job
  • akeneo-cron

Consulter les logs en cas d’erreur :

docker compose logs php
docker compose logs nginx
docker compose logs job-worker

13) Tester localement (avant le DNS)

curl -I http://127.0.0.1:8480

La commande doit retourner HTTP/1.1 302 Found avec un header Location
pointant vers /user/login. Cela confirme que PIM 7 fonctionne.

Identifiants par défaut PIM : admin / admin.
Si tu as changé le mot de passe dans PIM 6, les anciens identifiants sont conservés après la migration.

14) Basculer les enregistrements DNS

Créer (ou modifier) l’enregistrement A chez le registrar :

Sous-domaine Type Cible
{{DOMAINE_PIM}} A {{IP_NOUVEAU}}

Vérifier la propagation :

nslookup {{DOMAINE_PIM}}
dig +short {{DOMAINE_PIM}}

15) Activer Let’s Encrypt

/usr/local/vesta/bin/v-add-letsencrypt-domain {{USER_VESTA}} {{DOMAINE_PIM}}

Important : attendre que le DNS pointe vers {{IP_NOUVEAU}} avant de lancer
cette commande. Let’s Encrypt valide via HTTP.

16) Vérifier les crons PIM

Les crons PIM sont déjà configurés dans le docker-compose.yml (section 4.1)
via le conteneur akeneo-cron et le fichier config/crontab (section 4.3).
Ils démarrent automatiquement avec docker compose up -d.

Vérifier que le conteneur cron tourne :

docker compose ps cron
docker compose logs cron

Pour vérifier qu’un cron s’est bien exécuté, consulter les logs du conteneur PHP
(les commandes docker exec y redirigent leur sortie) :

docker compose logs --tail=20 php

Avantage par rapport au crontab hôte : tout est dans le compose.
Un docker compose down && up -d restaure les crons automatiquement.
Si tu migres le serveur à nouveau, pas de crontab à reconfigurer manuellement.

Récapitulatif de l’architecture finale

MyVestaCP (proxy + SSL)                Docker (application)
———————————————               ——————————
/home/{{USER_VESTA}}/web/                        /home/docker/akeneo/
└── {{DOMAINE_PIM}}/                ├── docker-compose.yml
    └── Nginx → 127.0.0.1:8480        ├── pim/           (code PIM 7)
                                        ├── config/nginx/  (vhost interne)
                                        ├── config/crontab (tâches planifiées)
                                        └── backup/

Docker Compose services (6 conteneurs) :
  - akeneo-mysql  (MySQL 8.0.30)
  - akeneo-es     (Elasticsearch 8.4.2)
  - akeneo-php    (PHP 8.1 FPM)
  - akeneo-nginx  (Nginx 1.25)
  - akeneo-job    (Job Worker / Queue Consumer)
  - akeneo-cron   (Crons PIM via docker exec)

Coexistence sur le serveur : Akeneo PIM, Odoo 8 et Odoo 17 tournent sur le même
serveur, complètement isolés dans leurs réseaux Docker respectifs :

  • akeneo-network : MySQL 8.0.30 + ES 8.4.2 + PHP + Nginx + Job Worker + Cron (port 8480)
  • odoo17-network : PostgreSQL 13 + Odoo 17 (ports 8069/8169/8269/8369)
  • odoo8-network : PostgreSQL 9.6 + Odoo 8 (ports 8469/8472)

En cas de problème

Erreur 502 Bad Gateway

Le conteneur PHP-FPM n’est pas prêt ou a crashé :

docker compose logs php
docker compose restart php nginx

Erreur « Connection refused » sur Elasticsearch

Elasticsearch n’a pas démarré (souvent un problème de mémoire ou de vm.max_map_count) :

# Vérifier les logs
docker compose logs elasticsearch

# Si erreur max_map_count, exécuter sur l'hôte :
sudo sysctl -w vm.max_map_count=262144
# Et pour le rendre permanent :
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf

Erreur de permission sur le filestore

sudo chown -R 33:33 /home/docker/akeneo/pim/var/
sudo chown -R 33:33 /home/docker/akeneo/pim/public/media/
docker compose restart php

Écran « Loading… » infini après la connexion

Le fichier extensions.json est manquant. Vérifier :

curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8480/js/extensions.json

Si le résultat est 404, regénérer le fichier (section 11.4) :

docker run -ti --rm -u root \
  -v /home/docker/akeneo/pim:/srv/pim \
  -w /srv/pim \
  node:18 node vendor/akeneo/pim-community-dev/frontend/build/update-extensions.js
chown 33:33 /home/docker/akeneo/pim/public/js/extensions.json

PHP-FPM crash en boucle avec « Address already in use (98) »

Vérifier que supervisord.conf contient bien command=/usr/local/sbin/php-fpm -F
(avec le flag -F pour le mode foreground). Sans ce flag, PHP-FPM se détache et
supervisord tente de le relancer en boucle (voir section 5.0.2).

Job-worker crash avec « No such transport « main_queue » »

PIM 7 a remplacé main_queue par 4 queues séparées.
Vérifier le command du service job-worker dans docker-compose.yml
(voir section 4.1).


À suivre

Akeneo PIM 7 est maintenant opérationnel en Docker avec reverse proxy et SSL.
Dans les prochains articles :

  • 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 : Odoo 8 en Docker — migration standalone vers Docker Compose.

 

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 *