Docs/ Infrastructure/ Volumes et montages

Volumes et montages

Conservez les données d'un redémarrage ou d'un déploiement à l'autre en montant dans le conteneur un volume Docker, un répertoire de l'hôte ou un fichier unique.

Pourquoi des montages

Chaque déploiement démarre un nouveau conteneur. Tout ce que l'application a écrit dans son propre système de fichiers disparaît avec le conteneur précédent : fichiers envoyés, bases SQLite, caches générés. Un montage rattache un stockage qui vit hors du conteneur, si bien que le conteneur suivant retrouve les mêmes données au même chemin.

Note
Les montages prennent effet au prochain déploiement. Après en avoir ajouté ou supprimé un, redéployez l'application (sh0 deploy my-api --wait). Les serveurs de bases de données créés par sh0 gèrent leur propre stockage : on ne leur ajoute pas de montage ici.

Types de montage

  • Volume -- un volume Docker nommé sh0-<app>-<nom>, créé par sh0. Le bon choix pour les données d'application.
  • Montage lié -- un répertoire de l'hôte monté dans le conteneur. Seuls les répertoires placés sous une racine autorisée sont acceptés (voir plus bas).
  • Montage de fichier -- un fichier unique dont vous saisissez le contenu dans le tableau de bord, pour un fichier de configuration. sh0 conserve le contenu et écrit le fichier sur l'hôte au déploiement.

Ajouter un montage

  1. Ouvrez l'application et allez dans l'onglet Volumes.
  2. Cliquez sur Ajouter un montage et choisissez le type.
  3. Renseignez Nom du montage (pour un volume, il entre dans le nom du volume Docker) et Chemin dans le conteneur, un chemin absolu comme /data.
  4. Pour un montage lié, indiquez le Chemin hôte ; pour un montage de fichier, le Contenu du fichier. Cochez Lecture seule si l'application ne doit pas y écrire.
  5. Validez avec Ajouter un montage, puis redéployez l'application.

La même chose par l'API (réponse réelle ; source est obligatoire et reste vide pour un nouveau volume) :

Terminal
$ curl -X POST https://panel.example.com/api/v1/apps/<app-id>/mounts \
    -H "Authorization: Bearer $SH0_TOKEN" -H "Content-Type: application/json" \
    -d '{"mount_type":"volume","name":"data","source":"","target":"/data"}'
{"data":{"app_id":"<app-id>","created_at":"2026-10-02 02:49:02","id":"0aa5cb7d-d97a-4cce-90c3-c61c338f0055","mount_type":"volume","name":"data","read_only":false,"source":"sh0-my-api-data","target":"/data"}}

mount_type vaut volume, bind ou file. source est vide pour un nouveau volume, porte le chemin hôte pour un montage lié et le contenu du fichier pour un montage de fichier. read_only vaut false par défaut. Créer un montage exige le rôle developer sur le projet de l'application.

Où vivent les données

  • Volume -- dans le répertoire propre à Docker, /var/lib/docker/volumes/sh0-<app>-<nom>/_data sur une installation Docker par défaut.
  • Montage de fichier -- /var/lib/sh0/file-mounts/<id-app>/<id-montage>/<nom du fichier>, monté sur le chemin du conteneur.
  • Montage lié -- là où vous le pointez, à condition que le chemin soit sous une racine autorisée : /var/lib/sh0/mounts par défaut pour une installation exécutée en root. La variable d'environnement SH0_BIND_MOUNT_ROOTS du service sh0 remplace cette liste (chemins séparés par des deux-points) ; vide, elle refuse tout montage lié.
Terminal
$ docker volume inspect sh0-my-api-data --format '{{.Mountpoint}} {{json .Labels}}'
/var/lib/docker/volumes/sh0-my-api-data/_data {"sh0.app_name":"my-api","sh0.managed":"true","sh0.mount_path":"/data"}

Un chemin hôte hors des racines autorisées est refusé dès la création du montage (réponse réelle) :

Response
{"error":{"code":"VALIDATION_ERROR","message":"Bind mount source '/etc' is outside the allowed roots (/var/lib/sh0/mounts). Set SH0_BIND_MOUNT_ROOTS to authorise another root."}}

sh0 ne modifie ni le propriétaire ni les droits des données montées. Le conteneur écrit avec l'utilisateur que déclare son image : une image exécutée en root écrit en root ; une image avec un USER non root a besoin d'un répertoire où cet utilisateur peut écrire.

Importer des données depuis une autre plateforme

Pour déplacer les fichiers d'une application depuis un autre hébergeur, archivez-les là-bas, puis déballez l'archive dans le volume sh0 avec sh0 volumes <app> import. L'archive (tar, brute ou compressée en gzip, bzip2 ou xz, jusqu'à 500 Mo) passe par Docker et arrive dans le volume lui-même, que l'application tourne, soit arrêtée ou ne soit pas encore déployée. L'ordre habituel d'une migration : sh0 apps create, sh0 volumes <app> create /data, l'import, puis le premier sh0 deploy.

Terminal
$ sh0 apps create my-site --image nginx:1.27-alpine --port 80
[ok] Created 'my-site' (25fc66d6-82b4-4aea-998b-d3cb0844162e), not deployed: set its variables, then `sh0 deploy my-site`
$ sh0 volumes my-site create /usr/share/nginx/html
[ok] Volume sh0-my-site-25fc66d6-usr-share-nginx-html created, mounted on /usr/share/nginx/html at the next deploy
$ sh0 volumes my-site import /usr/share/nginx/html site.tar.gz
[ok] Imported site.tar.gz (215 bytes) into sh0-my-site-25fc66d6-usr-share-nginx-html at /
$ sh0 deploy my-site --wait
Warning
Le piège des montages de répertoire (bind mounts). Sur la plateforme d'origine, vérifiez ce que le conteneur monte réellement avant d'archiver. Beaucoup de panneaux (Easypanel notamment) montent un répertoire de l'hôte (Type: bind) plutôt qu'un volume Docker : les fichiers vivent dans ce répertoire, et un volume Docker au nom voisin -- son /var/lib/docker/volumes/<nom>/_data -- peut être vide, d'autant plus visiblement que le conteneur est arrêté et que rien n'y écrit. Archiver _data migre alors un répertoire vide sans la moindre erreur. Même règle côté sh0 : ne copiez jamais de fichiers à la main dans _data, passez par l'import.

Lisez la source de chaque montage sur le conteneur lui-même, et archivez depuis ce chemin :

Terminal
$ docker inspect <container> --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}}{{println}}{{end}}'
$ tar -czf data.tar.gz -C <Source of the mount, as printed above> .

Supprimer un montage

Supprimer un montage dans l'onglet Volumes le retire du prochain déploiement. Pour un volume, sh0 tente aussi de supprimer le volume Docker, et Docker refuse tant qu'un conteneur l'utilise, ce qui est le cas tant que l'application tourne. Les données restent alors sur le disque et le volume réapparaît dans la liste avec un badge Docker, sans bouton de suppression. Pour supprimer réellement les données :

  1. Supprimez le montage dans l'onglet Volumes.
  2. Redéployez l'application, pour qu'aucun conteneur n'utilise plus le volume.
  3. Supprimez le volume par l'API :
Terminal
$ curl -X DELETE https://panel.example.com/api/v1/apps/<app-id>/volumes/sh0-my-api-data \
    -H "Authorization: Bearer $SH0_TOKEN"
Warning
Supprimer une application ne supprime pas ses volumes, ni depuis le tableau de bord ni avec sh0 delete. Seul l'appel d'API DELETE /api/v1/apps/<id>?delete_volumes=true les retire. Sinon, une future application du même nom avec le même nom de montage retrouve les anciennes données.

Limites

  • Ni limite de taille ni redimensionnement. Un volume grossit jusqu'à remplir le disque de l'hôte. Surveillez le disque du serveur.
  • Un montage par chemin du conteneur. Un second montage sur le même chemin est refusé : « A mount already targets '/data'. Delete it first, or choose another target. »
  • Pas d'instantané automatique. Un montage n'est pas sauvegardé de lui-même ; les sauvegardes se configurent à part, depuis la page Sauvegardes.
  • Local uniquement. Les volumes vivent sur le serveur qui exécute l'application ; il n'existe pas de stockage réseau ou objet pour les montages.