Docs/ Infraestructura/ Volúmenes y montajes

Volúmenes y montajes

Conserve los datos entre reinicios y despliegues montando en el contenedor un volumen de Docker, un directorio del host o un solo archivo.

Por qué montajes

Cada despliegue arranca un contenedor nuevo. Todo lo que la aplicación escribió en su propio sistema de archivos desaparece con el contenedor anterior: archivos subidos, bases SQLite, cachés generadas. Un montaje asocia un almacenamiento que vive fuera del contenedor, de modo que el siguiente contenedor encuentra los mismos datos en la misma ruta.

Note
Los montajes surten efecto en el próximo despliegue. Después de agregar o quitar uno, vuelva a desplegar la aplicación (sh0 deploy my-api --wait). Los servidores de bases de datos creados por sh0 gestionan su propio almacenamiento; no se les agregan montajes aquí.

Tipos de montaje

  • Volumen -- un volumen de Docker llamado sh0-<app>-<nombre>, creado por sh0. La opción adecuada para los datos de la aplicación.
  • Montaje de directorio -- un directorio del host montado en el contenedor. Solo se aceptan directorios bajo una raíz autorizada (ver más abajo).
  • Montaje de archivo -- un único archivo cuyo contenido escribe en el panel, para un archivo de configuración. sh0 guarda el contenido y escribe el archivo en el host durante el despliegue.

Agregar un montaje

  1. Abra la aplicación y vaya a la pestaña Volúmenes.
  2. Haga clic en Agregar montaje y elija el tipo.
  3. Complete Nombre del montaje (en un volumen, forma parte del nombre del volumen de Docker) y Ruta del contenedor, una ruta absoluta como /data.
  4. Para un montaje de directorio, indique la Ruta del host; para un montaje de archivo, el Contenido del archivo. Marque Solo lectura si la aplicación no debe escribir en él.
  5. Confirme con Agregar montaje y vuelva a desplegar la aplicación.

Lo mismo por la API (respuesta real; source es obligatorio y queda vacío para un volumen nuevo):

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 es volume, bind o file. source queda vacío para un volumen nuevo, lleva la ruta del host para un montaje de directorio y el contenido del archivo para un montaje de archivo. read_only es false por defecto. Crear un montaje requiere el rol developer en el proyecto de la aplicación.

Dónde viven los datos

  • Volumen -- en el directorio propio de Docker, /var/lib/docker/volumes/sh0-<app>-<nombre>/_data en una instalación de Docker predeterminada.
  • Montaje de archivo -- /var/lib/sh0/file-mounts/<id-app>/<id-montaje>/<nombre del archivo>, montado sobre la ruta del contenedor.
  • Montaje de directorio -- donde usted lo apunte, siempre que la ruta esté bajo una raíz autorizada: /var/lib/sh0/mounts por defecto en una instalación que se ejecuta como root. La variable de entorno SH0_BIND_MOUNT_ROOTS del servicio sh0 reemplaza esa lista (rutas separadas por dos puntos); vacía, rechaza todo montaje de directorio.
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"}

Una ruta del host fuera de las raíces autorizadas se rechaza al crear el montaje (respuesta real):

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 no cambia el propietario ni los permisos de los datos montados. El contenedor escribe con el usuario que declara su imagen: una imagen que se ejecuta como root escribe como root; una imagen con un USER no root necesita un directorio en el que ese usuario pueda escribir.

Importar datos desde otra plataforma

Para trasladar los archivos de una aplicación desde otro proveedor, archívelos allí y luego descomprima el archivo en el volumen de sh0 con sh0 volumes <app> import. El archivo (tar, sin comprimir o comprimido con gzip, bzip2 o xz, hasta 500 MB) pasa por Docker y llega al propio volumen, tanto si la aplicación está en marcha como detenida o aún sin desplegar. El orden habitual de una migración: sh0 apps create, sh0 volumes <app> create /data, la importación y, por último, el primer 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
La trampa de los montajes de directorio (bind mounts). En la plataforma de origen, compruebe qué monta realmente el contenedor antes de archivar. Muchos paneles (Easypanel entre ellos) montan un directorio del host (Type: bind) en lugar de un volumen Docker: los archivos viven en ese directorio, y un volumen Docker de nombre parecido -- su /var/lib/docker/volumes/<nombre>/_data -- puede estar vacío, y se nota más cuando el contenedor está detenido y nada escribe en él. Archivar _data migra entonces un directorio vacío sin un solo error. Lo mismo en sh0: nunca copie archivos a mano en _data, use la importación.

Lea el origen de cada montaje en el propio contenedor y archive desde esa ruta:

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> .

Eliminar un montaje

Eliminar un montaje en la pestaña Volúmenes lo quita del próximo despliegue. En un volumen, sh0 intenta además borrar el volumen de Docker, y Docker se niega mientras algún contenedor lo use, lo que ocurre mientras la aplicación está en marcha. Los datos quedan entonces en el disco y el volumen reaparece en la lista con una insignia Docker y sin botón de eliminar. Para borrar realmente los datos:

  1. Elimine el montaje en la pestaña Volúmenes.
  2. Vuelva a desplegar la aplicación, para que ningún contenedor use ya el volumen.
  3. Elimine el volumen por la 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
Eliminar una aplicación no elimina sus volúmenes, ni desde el panel ni con sh0 delete. Solo la llamada a la API DELETE /api/v1/apps/<id>?delete_volumes=true los quita. De lo contrario, una futura aplicación con el mismo nombre y el mismo nombre de montaje encuentra los datos anteriores.

Límites

  • Sin límite de tamaño ni redimensionamiento. Un volumen crece hasta llenar el disco del host. Vigile el disco del servidor.
  • Un montaje por ruta del contenedor. Un segundo montaje en la misma ruta se rechaza: «A mount already targets '/data'. Delete it first, or choose another target.»
  • Sin instantáneas automáticas. Un montaje no se respalda por sí solo; las copias de seguridad se configuran aparte, desde la página Copias de seguridad.
  • Solo local. Los volúmenes viven en el servidor que ejecuta la aplicación; no hay almacenamiento de red ni de objetos para los montajes.