Permissão de escrita nas pastas montadas
A partir da v2.154.0, quando a imagem declara um USER não-root, o Runner confere se esse usuário consegue escrever nas pastas montadas do host. Se não consegue, o Runner corrige sozinho o dono da pasta, avisa no log e segue com o deploy.
O problema
As pastas montadas moram no host, em /data/apps/<app>/. A aplicação roda no container com o USER da imagem, por exemplo node (uid 1000) ou nginx (uid 101).
Se a pasta do host pertence a root, esse usuário não consegue criar nem alterar arquivos nela. O sintoma depende da aplicação:
- o container entra em crashloop ao tentar criar um arquivo na inicialização;
- ou sobe normalmente e falha em silêncio, cada vez que tenta gravar.
Pastas antigas costumam estar nesse estado: foram criadas como root por uma versão anterior da imagem, que rodava como root, ou à mão no host.
O que o Runner faz
A conferência acontece em três momentos do deploy:
| Momento | O que acontece | Desde |
|---|---|---|
| Criação da pasta | Pasta que ainda não existe nasce com o dono do USER da imagem |
v2.146.0 |
| Antes do build | O Runner descobre o USER antes de construir a imagem e corrige o dono das pastas em que ele não escreveria |
v2.154.0 |
| Na subida do container | O Runner testa a escrita como o USER da imagem. Se falhar, corrige o dono e testa de novo |
v2.154.0 |
A correção antes do build evita esperar a imagem inteira ficar pronta para descobrir um problema de permissão. O teste na subida é a rede de segurança: cobre os casos em que o USER só fica conhecido depois do build.
De onde vem o `USER`
| Situação | Como o Runner descobre o USER |
|---|---|
Modo docker-build, USER numérico (USER 1000:1000) |
Lido do Dockerfile, antes do build |
Modo docker-build, USER por nome (USER node) |
O uid vem da imagem da versão anterior, antes do build. No primeiro deploy, só depois do build |
Modo docker-build, USER com variável (USER ${APP_UID}) |
Só depois do build |
Modo image: |
Lido da imagem já presente no servidor |
Sem USER, ou USER root |
Nada a fazer: root escreve em qualquer pasta |
No Dockerfile vale o último USER do último estágio. Um USER root usado só no estágio de build não conta.
O que é corrigido e o que nunca é tocado
O Runner troca o dono de toda a árvore da pasta (a pasta e o que tem dentro) para o uid e o gid do USER da imagem. Ele só faz isso quando:
- a pasta está dentro de
/data/apps/<app>/; - a montagem é de leitura e escrita (
rw). Montagens:ronão precisam de escrita e não são tocadas; - é uma pasta de verdade, não um link simbólico.
Durante a troca, o Runner:
- não segue links simbólicos, nem quando um link aparece no meio da árvore;
- não sai do sistema de arquivos da pasta;
- não entra em outra pasta montada que esteja dentro dela.
Nunca são tocados:
- caminhos fora de
/data/apps/<app>/, como/srv/dados:/app/dados; - volumes nomeados do Docker (
volume:<nome>:<destino>); - o código da aplicação em
src/.
O conteúdo dos arquivos não muda. Só o dono muda.
Como saber que aconteceu
A correção aparece como aviso no log do deploy e no runner-cron.log:
WARN volumes (antes do build): correção automática — posse de /data/apps/minha-api/out alinhada ao USER da imagem: 0 → 101:101 (2 entrada(s)), montada em /usr/share/nginx/html/outOu, quando acontece na subida do container:
WARN volumes: correção automática — posse de /data/apps/minha-api/out alinhada ao USER da imagem: 0 → 101:101 (2 entrada(s)), montada em /usr/share/nginx/html/outO aviso diz a pasta, o dono anterior, o dono novo, quantas entradas mudaram e onde a pasta está montada no container.
Quando a escrita continua falhando
Se o teste de escrita falha mesmo depois da correção, o problema não é o dono da pasta. O deploy para, e a versão anterior continua no ar:
pré-checagem de volumes: /usr/share/nginx/html/out (host /data/apps/minha-api/out) não aceita escrita do usuário 101 da imagem, mesmo depois de o runner alinhar a posse da pasta ao USER da imagem. Não é posse: confira se o sistema de arquivos aceita escrita (somente leitura, ACL, SELinux/AppArmor) e refaça o deploy.Causas comuns:
- disco ou partição montados como somente leitura;
- ACL que nega escrita para aquele uid;
- política de SELinux ou AppArmor.
Para ver, antes do deploy, quais pastas estão sem escrita (v2.155.0+):
runner preflight minha-apiO componente volumes lista cada pasta montada em que o USER da imagem não
escreve e diz que o deploy vai corrigir o dono. Ele não muda nada.
Para ver a causa da última falha:
runner debug last-error minha-apiDesde a v2.153.1, a saída mostra a causa e a fase em que o deploy falhou, e classifica a falha como Volume sem permissão de escrita, com os próximos passos. A mesma causa vai no campo cause do evento de falha em deploy-events.jsonl.
A pasta do volume não é apagada pela limpeza
Ao fim de cada deploy, o Runner remove da raiz da app pastas antigas de build, como out, public, dist, build, docs, content e .next.
Até a v2.154.0, essa limpeza também apagava a pasta de um volume declarado com um desses nomes, por exemplo out:/srv/out, com os dados dentro. Desde a v2.154.1, a pasta de origem de todo volume e asset do manifesto é preservada.
Se o seu manifesto declara um volume com um desses nomes, atualize o Runner antes do próximo deploy:
runner self-updateVersões
| Versão | Mudança |
|---|---|
| v2.146.0 | Pasta nova nasce com o dono do USER da imagem. Teste de escrita antes de subir o container |
| v2.153.1 | A causa de uma falha de volume aparece no evento e no runner debug last-error |
| v2.154.0 | Correção automática do dono antes do build e na subida do container |
| v2.154.1 | A limpeza de pastas antigas não apaga mais a pasta de um volume |
| v2.155.0 | runner preflight mostra as pastas sem escrita, sem alterar nada |
Ver também
- Ativos persistentes (`assets:`) — pastas de estado, cache e secrets da app
- Anatomia do .deploy.yml — o campo
volumes: - runner debug — a causa da última falha de deploy