SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-13

Installer Mealie sur un VPS avec Docker Compose

Installez Mealie sur votre VPS avec Docker Compose : importez une recette depuis son lien, gérez menus et courses, puis configurez nginx, TLS et les sauvegardes.

Ce que fait un gestionnaire de recettes auto-hébergé

Un gestionnaire de recettes auto-hébergé conserve vos recettes dans une base de données sur un serveur que vous possédez. Mealie est celui que la plupart des foyers finissent par choisir. Vous collez l’adresse d’une page de recette, puis Mealie en extrait les ingrédients, les étapes, le nombre de portions et le temps de cuisson. Il laisse de côté le récit et la publicité. Ce qui arrive dans votre collection, c’est la recette.

Le reste de l’application est réduit au minimum. Vous disposez d’un planning des repas de la semaine dans lequel vous faites glisser les recettes. Une liste de courses est ensuite générée à partir de ce planning. Chaque personne qui cuisine dispose de son propre login. L’ensemble s’exécute dans un seul conteneur et reste inactif entre les requêtes. Un VPS modeste peut donc l’héberger sans difficulté.

Ce guide utilise Docker Compose. Si les termes services: et volumes: ne vous sont pas familiers, lisez d’abord comment sont structurés les fichiers Docker Compose, car tout ce qui suit tient dans un seul fichier Compose et quatre commandes.

Installer Mealie avec Docker Compose

Mealie publie ses images dans le registre de conteneurs GitHub. En juillet 2026, le tag stable actuel est v3.22.0. Épinglez une version plutôt que d’utiliser latest : avec latest, un docker compose pull effectué un autre jour peut vous faire passer à une migration de base de données que vous n’étiez pas prêt à appliquer.

sudo mkdir -p /srv/mealie
cd /srv/mealie
sudo nano docker-compose.yml
services:
  mealie:
    image: ghcr.io/mealie-recipes/mealie:v3.22.0
    container_name: mealie
    restart: always
    ports:
      - "127.0.0.1:9925:9000"
    deploy:
      resources:
        limits:
          memory: 1000M
    volumes:
      - mealie-data:/app/data/
    environment:
      ALLOW_SIGNUP: "false"
      PUID: 1000
      PGID: 1000
      TZ: Europe/Amsterdam
      BASE_URL: https://recipes.example.com

volumes:
  mealie-data:

Deux lignes méritent votre attention avant le démarrage.

Le port est écrit 127.0.0.1:9925:9000 et non 9925:9000. Le conteneur écoute sur le port 9000 en interne, et l’hôte redirige le port 9925 vers celui-ci. Lier cette redirection à l’adresse loopback permet à nginx d’atteindre Mealie, sans l’exposer à Internet. Docker ajoute ses propres règles au packet filter. Ainsi, un port publié avec 9925:9000 reste accessible depuis l’extérieur même si votre firewall indique qu’il est fermé. Il est utile de comprendre ce comportement une fois : consultez pourquoi les ports Docker publiés ignorent ufw.

BASE_URL doit être l’adresse publique exacte que vous utiliserez, avec le scheme et sans slash final. Mealie construit les liens de réinitialisation des mots de passe et les liens d’invitation à partir de cette valeur. Si vous définissez http://localhost:9925, l’invitation envoyée à votre partenaire contiendra un lien qui ne fonctionnera que sur le serveur lui-même.

Démarrez le service et surveillez le premier boot.

sudo docker compose up -d
sudo docker compose logs -f mealie

Le premier démarrage crée la base de données SQLite et exécute les migrations, ce qui prend quelques secondes. Lorsque le journal se stabilise et n’affiche plus de lignes de migration, vérifiez l’application localement.

curl -I http://127.0.0.1:9925

Un 200 OK signifie que l’application fonctionne. Connection refused signifie que le conteneur n’est pas en cours d’exécution : exécutez sudo docker compose ps et consultez le code de sortie. Un conteneur arrêté avec le code 137 a été tué parce qu’il dépassait la limite mémoire de 1000M, ce qui arrive sur les offres les plus petites.

Première connexion et désactivation des inscriptions publiques

Le compte par défaut est changeme@example.com, avec le mot de passe MyPassword. Connectez-vous avec ce compte, puis modifiez immédiatement ces deux valeurs, car cette combinaison figure dans la documentation et se retrouve donc dans tous les scanners.

La valeur ALLOW_SIGNUP: "false" dans le fichier Compose est définie volontairement. Lorsque les inscriptions sont ouvertes, toute personne qui trouve l’adresse peut créer un compte dans votre boîte à recettes. Lorsqu’elles sont désactivées, vous ajoutez les utilisateurs depuis l’espace d’administration, qui génère un lien d’invitation à leur transmettre vous-même. Ce lien est construit à partir de BASE_URL, d’où l’importance de cette valeur. Si vous exécutez plusieurs applications sur le même serveur et souhaitez utiliser un seul mot de passe pour toutes, Mealie peut déléguer l’authentification à un fournisseur d’identité externe, par exemple une instance Authentik auto-hébergée.

Mealie regroupe les utilisateurs dans un foyer. Tous les membres d’un même foyer partagent la collection de recettes, le menu et la liste de courses, ce qui convient à une famille. Des foyers distincts sur le même serveur conservent des collections séparées, ce qui convient à une colocation lorsque personne ne parvient à se mettre d’accord sur les anchois.

L’importateur, qui est la raison de l’exécuter

Ouvrez la collection de recettes, choisissez de créer une recette à partir d’une URL, puis collez un lien. Mealie récupère la page et recherche les données structurées de la recette, c’est-à-dire le bloc lisible par machine que la plupart des sites de recettes intègrent pour les moteurs de recherche. Lorsque ce bloc est présent, l’importation est propre et immédiate.

Vous pouvez également importer une image ou du texte brut que vous collez, notamment la photographie d’une page de livre de cuisine. Ces méthodes passent par un traitement plus lent et nécessitent une vérification ultérieure, car une fraction manuscrite peut facilement être mal interprétée.

Les importations en masse se lancent depuis le même écran : collez une liste d’adresses, une par ligne, et Mealie les traite en arrière-plan. Une collection de deux cents favoris est ainsi transférée en une seule opération.

Plans de repas et liste de courses

Le planificateur de repas est un calendrier. Faites glisser une recette sur un jour pour la planifier. La liste de courses rassemble ensuite les ingrédients des recettes planifiées dans une seule liste et regroupe les doublons. Ainsi, deux recettes qui utilisent des oignons produisent une seule ligne au lieu de deux.

La liste est une page dynamique que vous pouvez consulter sur votre téléphone dans le magasin. Comme elle est hébergée sur votre propre serveur, tous les membres du foyer voient la même liste en temps réel. Lorsqu’une personne coche le lait, il disparaît aussi de l’écran des autres.

Placer nginx et TLS en frontal

Mealie utilise HTTP en clair et ne gère pas lui-même les certificats. Terminez le protocole TLS dans nginx, placé devant Mealie. Créez d’abord un enregistrement DNS de type A qui pointe vers votre serveur, car l’émission du certificat vérifie ce nom.

sudo apt update && sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mealie
server {
    listen 80;
    server_name recipes.example.com;

    client_max_body_size 64M;

    location / {
        proxy_pass http://127.0.0.1:9925;
        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;
    }
}
sudo ln -s /etc/nginx/sites-available/mealie /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

nginx -t affiche syntax is ok et test is successful : c’est le contrôle préalable. Rechargez nginx uniquement après sa réussite, car le rechargement d’une configuration incorrecte conserve l’ancienne configuration active et masque l’erreur jusqu’au prochain redémarrage.

client_max_body_size 64M est nécessaire, car la valeur par défaut de nginx est de 1 MB. L’envoi d’une photo de recette ou la restauration d’une sauvegarde depuis le navigateur transmet un corps de requête plus volumineux. Sans cette directive, nginx renvoie un 413 Request Entity Too Large, et non Mealie. Les journaux de l’application ne contiennent donc rien.

Émettez ensuite le certificat. Cette étape et son timer de renouvellement sont décrits dans émettre un certificat Let's Encrypt pour nginx avec certbot.

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d recipes.example.com

Certbot réécrit le server block pour écouter sur 443 et ajoute une redirection depuis le port 80. Accédez au site via https:// et vérifiez que le navigateur accepte le certificat. Si Mealie se charge, mais que ses propres liens vous redirigent vers http://, alors BASE_URL contient encore http. Corrigez cette valeur, puis exécutez sudo docker compose up -d pour recréer le conteneur avec la nouvelle valeur.

Mealie ne fonctionne pas sous un sous-chemin tel que example.com/recipes, car le frontend ne peut pas être servi depuis un sous-chemin. Utilisez un sous-domaine.

Sauvegardes et fonctionnement réel d’une restauration

Tout ce que Mealie gère se trouve dans /app/data/ à l’intérieur du conteneur. Il s’agit du volume mealie-data. Copiez ce volume pour copier ensemble les recettes, les images et la base de données.

sudo docker volume ls
sudo docker compose stop mealie
sudo docker run --rm -v mealie_mealie-data:/data -v "$PWD":/backup \
  alpine tar czf /backup/mealie-data.tgz -C /data .
sudo docker compose start mealie

Le nom du volume utilise le nom du projet comme préfixe. Le nom du projet correspond au répertoire qui contient le fichier compose. Depuis /srv/mealie, le volume est mealie_mealie-data. C’est pourquoi la première commande est docker volume ls : utilisez le nom qu’elle affiche, et non celui indiqué dans ce guide. Il est important d’arrêter d’abord le conteneur. SQLite peut être en cours d’écriture, et une copie effectuée à chaud peut parfois restaurer une base illisible.

Mealie possède également sa propre page de sauvegarde dans la zone d’administration. Elle crée une archive portable qui contient la base de données au format JSON ainsi que vos images. Utilisez cette méthode pour déplacer Mealie vers un autre serveur. Elle résiste à un changement de version, contrairement à une simple copie des fichiers. La restauration est volontairement destructive : elle supprime la base de données actuelle avant de charger l’archive. Cette opération est irréversible. Vous êtes déconnecté lorsque la restauration se termine.

Aucune de ces copies n’est une sauvegarde si elle reste sur le même serveur. Transférez l’archive ailleurs selon un calendrier défini. C’est l’objectif de sauvegardes chiffrées hors serveur avec restic.

Mise à jour de Mealie

cd /srv/mealie
sudo nano docker-compose.yml
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs -f mealie

Augmentez la version épinglée dans le fichier, puis téléchargez la nouvelle image et recréez le conteneur. Les migrations s’exécutent au premier démarrage de la nouvelle image. Faites une copie du volume avant tout changement de version majeure, car une migration qui échoue à mi-parcours peut laisser une base de données que l’ancienne image ne pourra plus ouvrir. Consultez les notes de version pour toutes les versions intermédiaires entre votre version actuelle et la nouvelle.

En cas d’échec de l’importation

Certains sites ne publient aucune donnée structurée sur les recettes. Mealie importe alors le titre, mais la liste des ingrédients reste vide. Ce comportement ne peut pas être modifié par la configuration. Saisissez plutôt le texte de la recette manuellement.

D’autres échecs sont dus à la protection contre les bots placée devant le site de recettes. Celui-ci renvoie alors une page de challenge à Mealie au lieu de la recette. Mealie se fait déjà passer pour un navigateur et fait tourner son user agent pour limiter ce problème. Si un site refuse toujours les requêtes, les options documentées consistent à faire passer le scraper par un proxy bénéficiant d’une meilleure réputation d’adresse, ou à exécuter une instance de FlareSolverr qui résout le challenge dans un vrai navigateur. Ces deux options sont facultatives et se configurent avec des variables d’environnement du conteneur.

Un import qui échoue parce que votre serveur ne peut pas joindre le site est un autre problème. Testez l’accès depuis le serveur avec curl -I https://the-site.example/recipe et lisez la ligne d’état avant d’incriminer le scraper.

Positionnement

Mealie est une bonne première application auto-hébergée pour un foyer, car les personnes avec qui vous vivez l’utiliseront sans qu’il soit nécessaire de le leur demander. Elle répond au même type de besoin que votre propre photothèque avec Immich, mais de façon beaucoup plus légère, et elle s’inscrit dans la liste plus large des services qui valent la peine d’être auto-hébergés cette année. Un seul petit serveur peut héberger les deux. Immich n’est pas le seul candidat pour ce second usage. Si vous hésitez encore, les besoins en mémoire et les commandes de sauvegarde de PhotoPrism et d’Immich présentent suffisamment de différences pour mériter une lecture avant de consacrer le reste du disque.

FAQ

Pourquoi l’importation d’une URL de recette échoue-t-elle ?

Deux causes sont fréquentes. Soit la page ne publie aucune donnée de recette structurée : le scraper ne trouve rien et vous obtenez un titre sans ingrédients. Soit une couche de protection contre les bots placée devant le site renvoie une page de challenge au lieu de la recette. Dans le second cas, vous pouvez configurer Mealie pour utiliser un proxy avec une meilleure réputation d’adresse, ou une instance FlareSolverr auto-hébergée qui résout le challenge dans un navigateur réel. Vérifiez d’abord que votre serveur peut accéder à la page avec curl -I avant de modifier quoi que ce soit.

Ai-je besoin de PostgreSQL, ou SQLite suffit-il ?

SQLite suffit pour un usage domestique et constitue la configuration par défaut. Passez à PostgreSQL lorsque le répertoire de données se trouve sur un stockage attaché au réseau, car SQLite sur un système de fichiers réseau génère des erreurs de base de données verrouillée et peut corrompre le fichier. Avec PostgreSQL, les restaurations nécessitent que l’utilisateur de la base de données soit un superuser, car la restauration supprime tout avant de charger l’archive.

Puis-je exécuter Mealie sans nom de domaine ?

Oui, sur votre propre réseau. Définissez BASE_URL avec l’adresse que vous saisirez réellement, par exemple http://192.168.1.20:9925, et n’utilisez pas nginx. Les liens d’invitation et de réinitialisation du mot de passe sont construits à partir de BASE_URL. Une valeur incorrecte produit donc des liens que personne d’autre ne peut ouvrir. N’exposez pas Mealie sur Internet en HTTP simple, car les identifiants de connexion sont alors transmis en clair.

Comment donner à ma famille ses propres comptes ?

Laissez ALLOW_SIGNUP défini sur "false", puis ajoutez les utilisateurs depuis la zone d’administration. Cela génère un lien d’invitation que vous pouvez leur envoyer. Placez dans le même household toutes les personnes qui partagent une cuisine, afin qu’elles partagent les recettes, le planning des repas et la liste de courses. Des households distincts sur un même serveur conservent des collections séparées.

Que deviennent mes recettes si j’arrête d’exécuter Mealie ?

Vous les récupérez. La sauvegarde de l’interface d’administration écrit vos données au format JSON. Mealie peut également exporter les recettes sous forme de fichiers markdown en texte brut, qui restent lisibles dans n’importe quel éditeur de texte, sans aucun logiciel supplémentaire. Effectuez un export avant d’en avoir besoin et vérifiez que vous pouvez l’ouvrir.