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 :ro nã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/out

Ou, 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/out

O 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-api

O 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-api

Desde 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-update

Versõ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

By Borlot.com.br on 14/10/2026