O que PUID e PGID fazem no Docker Compose
PUID e PGID não são configurações do Docker: são convenções do linuxserver.io. Veja por que os bind mounts ficam como 911:911 e como corrigir isso.
O que PUID e PGID realmente são
PUID e PGID são duas variáveis de ambiente que determinadas imagens de contentores leem no arranque. O Docker não as consulta. São uma convenção usada pelas imagens do linuxserver.io e por algumas outras. Por isso, uma imagem que não foi criada para as ler ignora-as silenciosamente.
Dentro de uma imagem do linuxserver.io existe um utilizador chamado abc, criado durante a compilação com UID (ID do utilizador) 911 e GID (ID do grupo) 911. O contentor inicia como root, executa os seus scripts de inicialização e um desses scripts altera o ID desse utilizador antes de qualquer outra operação:
groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abcA opção -o permite utilizar um ID que já está em uso noutro local. Depois disso, o script de inicialização reduz os privilégios e executa a aplicação como abc. Portanto, PUID=1000 nunca chega ao Docker. A variável altera o ID de um utilizador dentro do contentor antes do arranque da aplicação. Assim, todos os ficheiros que a aplicação criar no disco ficam pertencentes ao utilizador com o ID 1000. Se deixar PUID sem valor, abc mantém o ID 911. É por isso que um bind mount não configurado fica cheio de ficheiros pertencentes a 911:911.
Obtenha os dois números com id
Execute isto no host, como o utilizador proprietário dos diretórios de dados:
iduid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),988(docker)uid é o seu PUID e gid é o seu PGID. Num script, id -u e id -g imprimem apenas os números. Na maioria das imagens VPS novas, a primeira conta humana é 1000:1000, mas não assuma esse valor. Um servidor reconstruído ou uma segunda conta adicionada posteriormente pode usar 1001 ou um valor superior, e um número incorreto aqui é a causa de todo o problema. Se os seus serviços forem executados com uma conta de serviço dedicada em vez do seu próprio utilizador de login, execute id thatuser e obtenha os números a partir daí.
Por que os seus ficheiros aparecem como 911:911
ls -l mostra um ID numérico em vez de um nome quando não existe nenhuma conta no host correspondente a esse ID. Nenhuma conta no seu servidor tem o UID 911, por isso não existe um nome para mostrar. Use ls -ln para ver sempre os números e eliminar a ambiguidade:
ls -ln /srv/appdata/sonarrdrwxr-xr-x 2 911 911 4096 Aug 7 09:12 Backups
-rw-r--r-- 1 911 911 512 Aug 7 09:12 config.xmlEssa saída indica que o contentor foi executado com os valores predefinidos incorporados. Confirme isso a partir do interior do contentor, em vez de presumir:
docker exec sonarr id abc
docker compose logs sonarr | head -n 25O init do linuxserver regista o resultado no log de arranque em duas linhas:
User UID: 911
User GID: 911Se essas linhas mostrarem 911 depois de definir PUID=1000 no seu ficheiro Compose, a variável nunca chegou ao contentor. A causa habitual é ter editado docker-compose.yml e executado docker compose restart, que reutiliza o contentor existente com o ambiente original. As alterações de ambiente exigem docker compose up -d, que recria o contentor.
Por que você não consegue apagar um arquivo que o container criou
O kernel compara números, nunca nomes. O seu shell é executado como UID 1000. O arquivo pertence ao UID 911. O diretório que o contém é drwxr-xr-x e também pertence ao UID 911. Por isso, o grupo e os outros utilizadores têm permissões de leitura e execução, mas não de escrita. Apagar um arquivo exige permissão de escrita no diretório que o contém, não no arquivo. Por isso, você obtém este resultado mesmo quando o próprio arquivo parece inofensivo:
rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission deniedUm container que escreve encontra o mesmo problema pelo lado oposto. Se o diretório do host pertence ao seu utilizador com o modo 755 e a aplicação é executada como 911, a primeira escrita falha com Permission denied e a aplicação regista o erro com os seus próprios termos. Numa aplicação .NET como Sonarr ou Radarr, isso aparece como UnauthorizedAccessException: Access to the path '/data/downloads' is denied. A cadeia de permissões à frente do arquivo indica qual dos três conjuntos de permissões está efetivamente a ser aplicado ao seu caso. Ler drwxr-xr-x corretamente é o que transforma esse erro de misterioso em óbvio.
Este é especificamente um problema de bind mount. Quando o Docker cria um volume nomeado vazio e o monta sobre um caminho existente na imagem, copia para o volume o conteúdo desse caminho, incluindo o proprietário e os bits de permissão. Assim, a aplicação encontra um diretório que já possui. Um bind mount não recebe esse tratamento: o Docker monta o diretório do host exatamente como está. Essa diferença é uma das razões práticas para saber quando um bind mount é melhor do que um volume nomeado e quando não é.
Corrigir um diretório que já está com permissões incorretas
Definir PUID e PGID altera o comportamento da aplicação daqui em diante. Isso não corrige retroativamente os ficheiros que já estão no disco. Pare a stack, corrija manualmente o proprietário e inicie-a novamente:
docker compose down
sudo chown -R 1000:1000 /srv/appdata/sonarr
docker compose up -dUse sudo chown -R "$(id -u):$(id -g)" /srv/appdata/sonarr se preferir não escrever os números. Faça isto com o contentor parado, porque uma aplicação em execução que esteja a escrever durante um chown recursivo pode deixar a árvore de diretórios parcialmente corrigida e provocar uma segunda ronda de erros difíceis de interpretar.
O que PUID e PGID não corrigem
Esta é a parte que afeta quem fez tudo corretamente. O init da linuxserver executa chown em exatamente três caminhos durante o arranque: /app, /config e /defaults. Os seus mounts de media não estão nessa lista. /data, /downloads e /tv são entregues à aplicação sem alterações. Se o lado do host desses mounts tiver uma propriedade que impeça o utilizador do contentor de escrever, o contentor arranca corretamente, apresenta o UID correto no banner e falha na primeira importação.
Esse é o comportamento correto. Executar um chown recursivo numa biblioteca de media com doze terabytes sempre que o contentor arranca seria um desastre. Isto significa que a gestão dos diretórios de media é da sua responsabilidade. São esses os mounts onde os problemas de permissões ocorrem efetivamente.
Três formas de controlar o utilizador e quando aplicar cada uma
Variáveis de ambiente PUID e PGID
Isto funciona apenas em imagens cujo entrypoint as lê. É uma opção popular porque o contentor continua a iniciar como root, faz a sua própria configuração, corrige /config e só depois remove os privilégios. Docker Mods e scripts de inicialização personalizados continuam a funcionar. O custo é confiar numa convenção em vez de numa funcionalidade da plataforma, e os nomes das variáveis não são padronizados entre projetos.
A chave user: no Compose
Esta é uma funcionalidade real do Docker e funciona com qualquer imagem, porque o runtime do contentor aplica-a antes de o código da imagem ser executado:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
user: "1000:1000"O processo nunca é executado como root, nem por um instante, o que representa um ganho de segurança efetivo. Também interrompe qualquer parte do entrypoint que precise de root. Nas imagens linuxserver, o projeto suporta esta opção na medida do razoavelmente possível e apenas para as imagens que testou. As limitações são específicas: PUID e PGID deixam de ter efeito, Docker Mods não são executados, os serviços personalizados não são executados e você passa a ser responsável pelas permissões em todos os volumes montados. O padrão documentado combina a flag com um /run com permissão de escrita:
user: 1000:1000
tmpfs:
- /run:uid=1000,gid=1000,exec
security_opt:
- no-new-privileges=trueUm efeito apenas visual surpreende alguns utilizadores. Um user: numérico não tem uma entrada correspondente no /etc/passwd do contentor, pelo que as ferramentas internas apresentam whoami: cannot find name for user ID 1000. O ID é válido e o acesso aos ficheiros funciona normalmente. Apenas a resolução do nome falha.
Docker rootless
O Docker rootless executa o próprio daemon com o seu utilizador sem privilégios, pelo que nada no sistema é executado como root real. Isto altera completamente o cálculo da titularidade. O UID 0 do contentor é mapeado para o UID do utilizador no host que executa o Docker rootless, e o UID n do contentor, para qualquer n de 1 ou superior, é mapeado para subuid + (n - 1), em que subuid é a base do intervalo atribuído a si em /etc/subuid e /etc/subgid. O Docker espera pelo menos 65,536 IDs subordinados nesse ficheiro.
Leia novamente esse mapeamento, porque ele inverte a recomendação habitual. Com o Docker rootless, um contentor que escreve como root cria ficheiros pertencentes a si. Um contentor que escreve como UID 1000 cria ficheiros pertencentes a um ID subordinado próximo de 100999, ao qual a sua shell não consegue aceder. Por isso, o valor PUID correto num daemon rootful é incorreto neste caso. Os dois mecanismos resolvem o mesmo problema em camadas diferentes, e combiná-los sem verificar é uma forma de acabar com um diretório que precisa de sudo para ser removido. Se optar pelo modo rootless, teste a titularidade de um ficheiro criado no seu próprio servidor antes de migrar uma biblioteca para esse ambiente.
Na maioria das stacks self-hosted num único VPS, usar PUID e PGID num daemon rootful é a opção prática, porque é para isso que as imagens são criadas e documentadas. Use user: quando o README da imagem indicar que ela foi testada com essa opção ou quando estiver a executar uma imagem oficial do projeto upstream que não tenha suporte para PUID.
O caso da stack de media: um grupo partilhado entre contentores
Uma stack de media arr com Sonarr, Radarr e um cliente de downloads é onde isto deixa de ser teórico. O cliente de downloads escreve um ficheiro concluído em /data/downloads. O Sonarr cria depois um hardlink para esse ficheiro ou move-o para /data/media. Para o hardlink funcionar, os dois contentores precisam de acesso de escrita à mesma árvore. Se o cliente de downloads for executado como 1000 e o Sonarr como 1001, um deles será proprietário de ficheiros que o outro apenas pode ler.
A correção consiste num grupo partilhado que todos os contentores da stack usam como PGID:
sudo groupadd -g 13000 media
sudo usermod -aG media deploy
sudo chown -R deploy:media /srv/media
sudo find /srv/media -type d -exec chmod 2775 {} +
sudo find /srv/media -type f -exec chmod 0664 {} +O 2 inicial em 2775 é o bit setgid. Num diretório, significa que todos os ficheiros e subdiretórios novos criados dentro dele herdam o grupo media em vez do grupo primário do utilizador que os criou. Assim, a configuração mantém-se para novos downloads sem voltar a executar chown. Termine a sessão e inicie-a novamente, ou execute newgrp media, antes de verificar o seu próprio acesso. Um grupo adicionado com usermod -aG não aparece numa sessão de shell já aberta.
Dentro do contentor, groupmod -o -g 13000 abc renumera o grupo abc para 13000. Assim, abc escreve com o mesmo GID que o grupo media no host. Cada contentor da stack mantém o seu próprio PUID e partilha esse único PGID.
Depois, defina UMASK=002 em todos os contentores linuxserver da stack. Este é o passo que muitas pessoas esquecem. O valor predefinido nestas imagens é UMASK=022, que remove o bit de escrita do grupo de todos os ficheiros novos. Assim, os ficheiros ficam com 0644 e a partilha configurada deixa de ter efeito. 002 produz ficheiros 0664 e diretórios 0775, e o grupo pode escrever:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=Etc/UTC
volumes:
- /srv/appdata/sonarr:/config
- /srv/media:/data
restart: unless-stoppedEstes dois valores devem ficar num ficheiro .env junto ao ficheiro Compose, para que toda a stack leia uma única definição:
PUID=1000
PGID=13000O Compose lê automaticamente esse ficheiro para a substituição ao estilo ${PUID}. É o mesmo mecanismo usado para credenciais. As boas práticas de manter os valores fora de docker-compose.yml e num ficheiro .env também se aplicam aqui. A diferença é que estes dois números não são secretos.
Verifique o funcionamento de ponta a ponta em vez de confiar apenas na configuração. Escreva um ficheiro dentro de um contentor e leia-o a partir do host:
docker exec sonarr touch /data/downloads/permtest
ls -ln /srv/media/downloads/permtestUm resultado correto mostra o seu PUID como proprietário, 13000 como grupo e -rw-rw-r-- como modo. Se o grupo apresentado for 1000, falta o bit setgid nesse diretório. Se o modo apresentado for -rw-r--r--, a variável UMASK não teve efeito. Verifique se recriou o contentor em vez de apenas o reiniciar. Remova o ficheiro de teste com rm /srv/media/downloads/permtest quando terminar.
Quais imagens usam cada variável
As imagens linuxserver.io usam PUID, PGID e UMASK. O Paperless-ngx usa nomes diferentes para a mesma finalidade: USERMAP_UID e USERMAP_GID, ambos com o valor predefinido 1000. A documentação do projeto indica que deve obter esses valores de id -u e id -g. Muitas imagens oficiais dos projetos upstream, incluindo as imagens comuns de bases de dados e servidores web, incluem um utilizador fixo e esperam que utilize user: ou que não o altere.
Por isso, consulte o README de cada imagem antes de copiar um bloco de ambiente entre projetos. O Docker passa qualquer variável de ambiente definida para qualquer contentor, independentemente de existir algo no contentor que a leia. Uma PUID que nenhum componente consome não produz erro, aviso nem efeito. O contentor é executado com o utilizador definido pela última instrução do seu próprio Dockerfile. Pode confirmar qual é esse utilizador através da propriedade dos ficheiros que o contentor escreve.
FAQ
Por que os meus ficheiros Docker pertencem a 911:911?
911 é o UID e o GID do utilizador abc integrado nas imagens linuxserver.io. Isto significa que o contentor arrancou sem PUID e PGID definidos, pelo que o script de inicialização manteve os valores predefinidos integrados. ls -l mostra os números sem conversão porque nenhuma conta no seu host tem o ID 911. Por isso, não existe um nome para apresentar. Defina PUID e PGID com o resultado de id, recrie o contentor com docker compose up -d e corrija os ficheiros existentes com sudo chown -R 1000:1000 no diretório afetado.
PUID e PGID funcionam em todas as imagens Docker?
Não. Não são uma funcionalidade do Docker, e o Docker nunca os lê. Funcionam apenas em imagens cujo próprio entrypoint os lê e chama usermod e groupmod antes de iniciar a aplicação. Esse é o caso da família linuxserver.io e de alguns projetos que copiaram o padrão. Outros projetos usam nomes diferentes, como USERMAP_UID e USERMAP_GID no paperless-ngx. Numa imagem que não leia nenhuma dessas variáveis, elas são aceites e ignoradas sem qualquer aviso.
Devo usar PUID e PGID ou a chave user: no Docker Compose?
Use PUID e PGID quando a imagem os suportar. O entrypoint continua a ser executado como root durante tempo suficiente para corrigir /config e iniciar corretamente os seus próprios serviços. Use user: quando a imagem não tiver suporte para PUID ou quando o README da imagem indicar que a execução sem root foi testada. Numa imagem linuxserver, definir user: torna PUID e PGID inativos, impede a execução de Docker Mods e de serviços personalizados e faz com que as permissões de todos os volumes montados fiquem sob a sua responsabilidade.
O Sonarr tem o PUID correto, mas continua sem conseguir mover ficheiros. Qual é o problema?
Verifique três pontos, pela ordem seguinte. Primeiro, a própria montagem de media: a inicialização só executa chown em /app, /config e /defaults. Por isso, /data ou /downloads mantém a propriedade existente no host. Segundo, o grupo partilhado: se o cliente de downloads e o Sonarr forem executados com GIDs diferentes, nenhum poderá modificar os ficheiros do outro. Defina o mesmo PGID em todos os contentores da stack. Terceiro, o umask: o valor predefinido da imagem, UMASK=022, cria ficheiros como 0644 sem o bit de escrita para o grupo. Isso impede completamente a utilização de um grupo partilhado. Defina UMASK=002 e ative o bit setgid nos diretórios com chmod 2775 para que os ficheiros novos herdem o grupo.