Docs/ Infraestrutura/ Volumes e montagens

Volumes e montagens

Preserve dados entre reinícios e implantações montando no contêiner um volume Docker, um diretório do host ou um único arquivo.

Por que montagens

Cada implantação inicia um contêiner novo. Tudo o que o app gravou no próprio sistema de arquivos desaparece com o contêiner anterior: uploads, bancos SQLite, caches gerados. Uma montagem associa um armazenamento que vive fora do contêiner, de modo que o próximo contêiner encontra os mesmos dados no mesmo caminho.

Note
As montagens passam a valer na próxima implantação. Depois de adicionar ou remover uma, reimplante o app (sh0 deploy my-api --wait). Os servidores de banco de dados criados pelo sh0 gerenciam o próprio armazenamento; não se adicionam montagens para eles aqui.

Tipos de montagem

  • Volume -- um volume Docker chamado sh0-<app>-<nome>, criado pelo sh0. A escolha certa para dados do aplicativo.
  • Bind Mount -- um diretório do host montado no contêiner. Só são aceitos diretórios sob uma raiz autorizada (veja abaixo).
  • Montagem de arquivo -- um único arquivo cujo conteúdo você digita no painel, para um arquivo de configuração. O sh0 guarda o conteúdo e grava o arquivo no host na implantação.

Adicionar uma montagem

  1. Abra o app e vá até a aba Volumes.
  2. Clique em Adicionar montagem e escolha o tipo.
  3. Preencha Nome da montagem (num volume, ele entra no nome do volume Docker) e Caminho do contêiner, um caminho absoluto como /data.
  4. Para um bind mount, informe o Caminho do host; para uma montagem de arquivo, o Conteúdo do arquivo. Marque Somente leitura se o app não deve gravar nele.
  5. Confirme com Adicionar montagem e reimplante o app.

O mesmo pela API (resposta real; source é obrigatório e fica vazio para um volume novo):

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 é volume, bind ou file. source fica vazio para um volume novo, traz o caminho do host para um bind mount e o conteúdo do arquivo para uma montagem de arquivo. read_only é false por padrão. Criar uma montagem exige o papel developer no projeto do app.

Onde ficam os dados

  • Volume -- no diretório do próprio Docker, /var/lib/docker/volumes/sh0-<app>-<nome>/_data numa instalação padrão do Docker.
  • Montagem de arquivo -- /var/lib/sh0/file-mounts/<id-app>/<id-montagem>/<nome do arquivo>, montado no caminho do contêiner.
  • Bind Mount -- onde você apontar, desde que o caminho esteja sob uma raiz autorizada: /var/lib/sh0/mounts por padrão numa instalação executada como root. A variável de ambiente SH0_BIND_MOUNT_ROOTS do serviço sh0 substitui essa lista (caminhos separados por dois-pontos); vazia, recusa todo bind mount.
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"}

Um caminho do host fora das raízes autorizadas é recusado na criação da montagem (resposta 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."}}

O sh0 não altera dono nem permissões dos dados montados. O contêiner grava com o usuário que a imagem declara: uma imagem executada como root grava como root; uma imagem com USER não root precisa de um diretório em que esse usuário possa gravar.

Importar dados de outra plataforma

Para mover os arquivos de uma aplicação a partir de outro provedor, arquive-os lá e depois descompacte o arquivo no volume do sh0 com sh0 volumes <app> import. O arquivo (tar, sem compressão ou comprimido com gzip, bzip2 ou xz, até 500 MB) passa pelo Docker e chega ao próprio volume, esteja a aplicação em execução, parada ou ainda não implantada. A ordem habitual de uma migração: sh0 apps create, sh0 volumes <app> create /data, a importação e, por fim, o primeiro 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
A armadilha dos montagens de diretório (bind mounts). Na plataforma de origem, verifique o que o contêiner realmente monta antes de arquivar. Muitos painéis (o Easypanel, por exemplo) montam um diretório do host (Type: bind) em vez de um volume Docker: os arquivos ficam nesse diretório, e um volume Docker de nome parecido -- o seu /var/lib/docker/volumes/<nome>/_data -- pode estar vazio, o que se nota ainda mais com o contêiner parado e nada escrevendo nele. Arquivar _data migra então um diretório vazio sem um único erro. O mesmo vale no sh0: nunca copie arquivos à mão para _data, use a importação.

Leia a origem de cada montagem no próprio contêiner e arquive a partir desse caminho:

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

Remover uma montagem

Excluir uma montagem na aba Volumes a retira da próxima implantação. Num volume, o sh0 também tenta apagar o volume Docker, e o Docker recusa enquanto algum contêiner o usa, o que acontece enquanto o app está rodando. Os dados ficam então no disco e o volume reaparece na lista com um selo Docker e sem botão de excluir. Para apagar de fato os dados:

  1. Exclua a montagem na aba Volumes.
  2. Reimplante o app, para que nenhum contêiner use mais o volume.
  3. Exclua o volume pela 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
Excluir um app não exclui seus volumes, nem pelo painel nem com sh0 delete. Só a chamada de API DELETE /api/v1/apps/<id>?delete_volumes=true os remove. Caso contrário, um futuro app com o mesmo nome e o mesmo nome de montagem encontra os dados antigos.

Limites

  • Sem limite de tamanho nem redimensionamento. Um volume cresce até encher o disco do host. Acompanhe o disco do servidor.
  • Uma montagem por caminho do contêiner. Uma segunda montagem no mesmo caminho é recusada: “A mount already targets '/data'. Delete it first, or choose another target.”
  • Sem snapshot automático. Uma montagem não é copiada sozinha; os backups são configurados à parte, na página Backups.
  • Somente local. Os volumes ficam no servidor que executa o app; não há armazenamento de rede nem de objetos para montagens.