SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor

O que PUID e PGID fazem no Docker Compose

PUID e PGID nao sao configuracoes do Docker: sao convencoes de entrypoint da linuxserver.io. Entenda por que bind mounts ficam como 911:911 e corrija isso.

O que PUID e PGID realmente são

PUID e PGID são duas variáveis de ambiente que determinadas imagens de contentor leem no arranque. O próprio Docker nunca as consulta. Trata-se de uma convenção usada pelas imagens linuxserver.io e por algumas outras. Por isso, uma imagem que não tenha sido concebida para as ler ignora-as silenciosamente.

Dentro de uma imagem linuxserver.io existe um utilizador chamado abc, criado durante a compilação com UID (ID de utilizador) 911 e GID (ID de 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 a aplicação arrancar:

groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abc

A opção -o permite usar um ID que já esteja em uso noutro local. Depois disso, a inicialização remove os privilégios e executa a aplicação como abc. Assim, o Docker passa PUID=1000 para o contentor como qualquer outra variável de ambiente e nunca atua sobre o seu valor. A variável altera o ID de um utilizador dentro do contentor antes de a aplicação arrancar. Num daemon rootful padrão, sem remapeamento de namespaces de utilizadores, isto significa que os ficheiros criados normalmente ficam no disco com o proprietário 1000. Escrever num ficheiro existente com permissões de escrita não altera o respetivo proprietário. O Docker rootless altera esse número, como explicado mais abaixo. Se deixar PUID por definir, abc mantém o valor 911. É por isso que um bind mount não configurado fica cheio de ficheiros pertencentes a 911:911.

Obtenha os seus dois números com id

Execute isto no host, com o utilizador proprietário dos diretórios de dados:

id
uid=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 mostram apenas os números. Na maioria das imagens VPS novas, a primeira conta humana é 1000:1000, mas não assuma esse valor. A atribuição depende das contas e da configuração existentes: um servidor reinstalado pode atribuir novamente o ID 1000, enquanto uma conta criada mais tarde recebe um ID disponível superior. Se os seus serviços forem executados com uma conta de serviço dedicada em vez do seu próprio utilizador de início de sessão, execute id thatuser e use os números apresentados.

Por que os seus ficheiros aparecem como 911:911

ls -l mostra um ID numérico em vez de um nome quando nenhuma conta do host corresponde a esse ID. Se nenhuma conta do host corresponder ao UID 911, não há nenhum nome para mostrar. Use ls -ln para ver sempre os números e eliminar a ambiguidade:

ls -ln /srv/appdata/sonarr
drwxr-xr-x 2 911 911 4096 Aug  7 09:12 Backups
-rw-r--r-- 1 911 911  512 Aug  7 09:12 config.xml

Esse resultado corresponde aos valores predefinidos integrados no contentor, mas os ficheiros existentes, por si só, não provam com que utilizador a aplicação é executada atualmente. O config.xml nessa listagem também é o ficheiro que contém as definições de autenticação. Isso é importante na primeira vez que abrir a interface Web e descobrir que Sonarr e Radarr são distribuídos sem nome de utilizador ou palavra-passe predefinidos. Confirme a informação a partir do interior do contentor em vez de fazer suposições:

docker exec sonarr id abc
docker compose logs sonarr | head -n 25

A inicialização do linuxserver escreve o resultado no log de arranque, em duas linhas:

User UID:    911
User GID:    911

Se essas linhas mostrarem 911 depois de definir PUID=1000 no seu ficheiro Compose, o contentor em execução não está a usar o valor que definiu. 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 ao ambiente exigem docker compose up -d, que recria o contentor. A outra causa é intencional: quando uma imagem é executada no modo somente leitura ou no modo sem root, a inicialização ignora totalmente a renumeração, mesmo quando a variável está presente no contentor. No modo somente leitura, o banner mostra o ID inalterado de abc; no modo sem root, mostra o proprietário de /run.

Por que não é possível apagar um ficheiro escrito pelo contentor

O kernel compara números, nunca nomes. A sua shell é executada como UID 1000. O ficheiro pertence ao UID 911. O diretório que o contém é drwxr-xr-x e também pertence a 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 ficheiro exige permissão de escrita no diretório que o contém, não no ficheiro. Por isso, obtém este resultado mesmo quando o próprio ficheiro parece inofensivo:

rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission denied

Um contentor que escreve encontra o mesmo bloqueio pelo lado oposto. Se o diretório do host pertencer ao seu utilizador e tiver o modo 755, e a aplicação for executada como 911, a primeira escrita falha com Permission denied e a aplicação comunica o erro nos 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 antes do nome do ficheiro 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á lhe pertence. 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 é.

Corrigindo um diretório que já está incorreto

Definir PUID e PGID altera o comportamento da aplicação a partir desse momento. Isso não corrige retroativamente todos os ficheiros já existentes no disco, embora o script de inicialização corrija a propriedade em caminhos específicos descritos abaixo. Pare a stack, corrija a propriedade manualmente e inicie-a novamente:

docker compose down
sudo chown -R 1000:1000 /srv/appdata/sonarr
docker compose up -d

Use sudo chown -R "$(id -u):$(id -g)" /srv/appdata/sonarr se preferir não introduzir 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 série confusa de erros.

O que PUID e PGID não corrigem

Esta é a parte que afeta quem fez tudo corretamente. O script de configuração de utilizadores da imagem base linuxserver executa chown em três caminhos durante o arranque: /app, /config e /defaults. Os 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 normalmente, 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 configuração dos diretórios de media é da sua responsabilidade. São também os mounts onde os problemas de permissões realmente ocorrem. Como este tipo de falha aparece silenciosamente num log da aplicação horas depois de o contentor parecer saudável, um teste periódico de escrita integrado com ntfy no seu próprio VPS, enviando alertas para o seu telemóvel é uma forma simples de detetar o problema antes que uma semana de episódios em falta o revele.

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 arrancar como root, executa a sua própria configuração, corrige /config e só depois reduz os privilégios. Docker Mods e scripts de inicialização personalizados continuam a funcionar. O custo é depender de uma convenção em vez de uma 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 própria 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 real de segurança. No entanto, isto também interrompe tudo no entrypoint que necessite de root. Nas imagens linuxserver, o projeto disponibiliza este suporte numa base de esforços razoáveis e apenas para as imagens que testou. As limitações são específicas: PUID e PGID deixam de ter qualquer efeito, Docker Mods não são executados, os serviços personalizados não são executados e passa a ser responsável pelas permissões de todos os volumes montados. O padrão documentado combina a flag com um /run com permissões de escrita:

user: 1000:1000
tmpfs:
  - /run:uid=1000,gid=1000,exec
security_opt:
  - no-new-privileges=true

Um efeito secundário apenas visual surpreende alguns utilizadores. Um user: numérico sem uma entrada correspondente no /etc/passwd do contentor faz com que as ferramentas no interior mostrem 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 como o seu utilizador sem privilégios, pelo que o daemon e os contentores funcionam sem privilégios de root no host. Isto altera completamente a correspondência de proprietários. O UID 0 do contentor é mapeado para o UID do utilizador no host que executa o Docker rootless. O UID do contentor n, para qualquer n de 1 ou superior, é mapeado para subuid + (n - 1), onde subuid é a base do intervalo atribuído ao utilizador em /etc/subuid. Os IDs de grupo seguem a mesma regra usando /etc/subgid. O Docker espera pelo menos 65,536 IDs subordinados nesse local.

Leia novamente esse mapeamento, porque ele inverte a recomendação habitual. Com o Docker rootless, um contentor que crie um ficheiro como root normalmente produz um ficheiro pertencente ao utilizador. Um contentor que crie um ficheiro como UID 1000 normalmente produz um ficheiro pertencente a um ID subordinado, como 100999 quando o intervalo começa em 100000. O acesso a partir da sua shell depende das permissões e das ACLs. A eliminação normalmente depende do diretório que contém o ficheiro, pelo que pode eliminar esse ficheiro a partir de um diretório que controla. Portanto, o valor de PUID correto num daemon rootful é normalmente o valor errado neste caso. Os dois mecanismos alteram o proprietário em camadas diferentes e combiná-los sem verificação pode impedir a escrita nos dados montados. Se optar pelo modo rootless, teste o proprietário de um ficheiro criado no seu próprio servidor antes de migrar uma biblioteca para lá.

Para a maioria das stacks self-hosted num único VPS, PUID e PGID num daemon rootful são a opção pragmática, porque é para isso que as imagens são construídas e documentadas. Use user: quando o README da imagem indicar que ela foi testada para esse modo, ou quando estiver a executar uma imagem oficial do projeto original que não tenha suporte para PUID. Um workspace de documentos como uma instância self-hosted do AFFiNE num único VPS enquadra-se no segundo caso, porque nenhum dos seus contentores lê PUID e o proprietário do diretório da base de dados e dos ficheiros enviados é definido pelo runtime, não por algo no bloco de ambiente. O mesmo acontece com um serviço de suporte self-hosted Chatwoot, em que o contentor Rails e o seu worker Sidekiq escrevem no mesmo diretório de uploads e nenhum deles lê PUID. Por isso, esse diretório tem de corresponder ao utilizador com que a imagem já é executada. Isto também não muda numa stack mais recente. Assim, dar a cada pessoa da equipa o seu próprio agente OneCLI isolado deixa os diretórios de workspace individuais e o diretório de dados do Postgres pertencentes ao utilizador com que cada imagem já é executada. Isto transforma a situação num problema de user: e chown, não num problema de PUID. Coloque uma única API self-hosted à frente do Codex, Claude Code e Hermes e o seu entrypoint passa a gerir várias identidades: o contentor tem de arrancar como root, a consola e o gateway são executados como um utilizador sem privilégios e cada sessão de agente é executada como o seu próprio utilizador. Não adicione user: a essa implementação; o entrypoint gere o proprietário do volume de dados.

O caso de uma 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 teoria. O cliente de downloads escreve um ficheiro concluído em /data/downloads. O Sonarr cria então 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 correr como 1000 e o Sonarr como 1001, sem permissões de escrita partilhadas pelo grupo, um deles será o proprietário dos ficheiros que o outro apenas consegue ler.

A correção é um 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, isto significa que todos os ficheiros e subdiretórios novos criados dentro dele herdam o grupo media, em vez do grupo primário próprio do criador. Assim, a configuração continua a funcionar com novos downloads sem ser necessário executar novamente 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, para que abc escreva com o mesmo GID que o grupo media do host. Cada contentor linuxserver que acede a esta árvore mantém o seu próprio PUID e partilha esse PGID. Isto inclui contentores mais abaixo na cadeia que apenas leem a biblioteca concluída, como o próprio Jellyfin. Um frontend como Halcyon, que disponibiliza essa biblioteca como uma videoteca dos anos 90 navegável, liga-se ao Jellyfin por HTTP usando HALCYON_JELLYFIN_URL. A sua implementação padrão não precisa do PGID de media partilhado nem de uma montagem do diretório de media.

Depois, defina UMASK=002 em todos os contentores linuxserver da stack. Este é o passo que muitas pessoas esquecem. Se ficar indefinido, a aplicação mantém o umask herdado, a menos que algo o altere, e o valor de exemplo apresentado nos READMEs das imagens é UMASK=022. Com um umask efetivo de 022, uma aplicação que peça 0666 para um ficheiro novo obtém 0644 quando não existe uma ACL predefinida. O restante grupo continua a poder ler o ficheiro, mas não pode alterá-lo, o que desfaz grande parte da partilha configurada. Com 002 e sem uma ACL predefinida, uma aplicação que peça os valores habituais 0666 para ficheiros e 0777 para diretórios obtém ficheiros 0664 e, dentro da árvore setgid, diretórios 2775, permitindo ao grupo escrever. Um umask apenas remove bits. Por isso, uma aplicação que peça 0600 continua a obter 0600. Um diretório com uma ACL predefinida ignora totalmente o umask. Adicione a variável junto de PUID e PGID:

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

Esses dois valores devem ficar num ficheiro .env junto do ficheiro Compose, para que toda a stack leia uma única definição:

PUID=1000
PGID=13000

O Compose lê automaticamente esse ficheiro para fazer substituições do tipo ${PUID}, usando o mesmo mecanismo aplicado às credenciais. As 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 novo como o utilizador da aplicação, aplicando o umask configurado, e leia-o a partir do host. Use um nome de ficheiro de teste que ainda não exista:

docker exec --user abc sonarr sh -c 'umask "$UMASK" && touch /data/downloads/permtest'
ls -ln /srv/media/downloads/permtest

Um resultado correto mostra o seu PUID como proprietário, 13000 como grupo e -rw-rw-r-- como modo. Se o grupo aparecer como 1000, execute ls -ldn /srv/media/downloads: ou falta o bit setgid — o s em drwxrwsr-x — nesse diretório, ou o próprio diretório ainda pertence ao grupo 1000 em vez de media. Os comandos chown e chmod 2775 acima corrigem ambos os problemas. Se o modo aparecer como -rw-r--r--, verifique o valor UMASK do contentor e a ACL predefinida do diretório. Recrie o contentor se o ambiente estiver desatualizado. Este teste verifica a criação como abc com a máscara configurada. O modo de criação próprio da aplicação e o umask efetivo continuam a determinar as permissões dos seus ficheiros. Remova o ficheiro de teste com rm /srv/media/downloads/permtest quando terminar.

Quais imagens utilizam cada variável

As imagens linuxserver.io utilizam PUID, PGID e UMASK. O Paperless-ngx utiliza nomes diferentes para a mesma ideia: USERMAP_UID e USERMAP_GID, ambos com o valor predefinido 1000. A documentação do projeto indica que deve obter esses valores a partir de id -u e id -g. Os servidores de fotografias também apresentam diferenças: o PhotoPrism tem o seu próprio par PHOTOPRISM_UID e PHOTOPRISM_GID, enquanto o Immich não fornece um equivalente e deixa o utilizador do contentor ao valor da chave user: do Docker. Assim, escolher entre PhotoPrism e Immich também determina qual destes mecanismos terá de manter para a maior biblioteca do servidor. Muitas imagens oficiais dos projetos originais, incluindo as imagens comuns de bases de dados e servidores Web, incluem um utilizador fixo e esperam que utilize user: ou que não altere essa configuração. As implementações pequenas de uma única aplicação levantam a mesma questão. Por isso, quando configurar um gestor de treinos openGym autoalojado, confirme primeiro com que utilizador o respetivo contentor é executado antes de associar um bind mount, porque o diretório que contém a base de dados herda essa definição, independentemente de definir PUID. Um relay de acesso remoto pertence à mesma categoria. Assim, quando executar o seu próprio servidor relay RustDesk, o par de chaves Ed25519 que o hbbs grava no primeiro arranque aparece no bind mount com a propriedade do utilizador que a imagem utiliza. Pode corrigir isto depois com um chown executado no host, ou escolher a identidade antecipadamente com a chave user:, porque essa imagem não define um utilizador próprio, desde que o diretório montado tenha permissões de escrita para o ID escolhido. O mesmo se aplica à infraestrutura que adicionar mais tarde. Colocar o Authentik à frente das suas aplicações para fornecer um único início de sessão significa executar imagens oficiais do authentik e do Postgres que não leem PUID. A propriedade dos volumes resulta dos utilizadores com que os respetivos processos são executados e da configuração realizada pelos seus entrypoints. O Authentik removeu a dependência do Redis na versão 2025.10.

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 que defina para qualquer contentor, independentemente de existir algum processo que a leia. Uma PUID que nenhum componente consome não produz erro, aviso nem efeito. O contentor começa com o utilizador selecionado pela definição user: do Docker ou pelo Dockerfile. Em seguida, o entrypoint pode abandonar privilégios antes de iniciar a aplicação. Faça essa verificação antes de adicionar qualquer componente ao servidor, incluindo uma stack de análise de segurança open-kritt autoalojada, na qual o ficheiro Compose indica se as imagens suportam PUID ou se a propriedade dos diretórios montados é definida pelas próprias imagens.

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. É normal que corresponda aos valores predefinidos integrados, mas os ficheiros podem manter essa propriedade depois de alterar o PUID e o PGID do contentor. ls -l mostra os números brutos quando nenhuma conta no seu host corresponde a esses IDs, pelo que não existe nenhum 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.

O PUID e o 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, porque o entrypoint continua a ser executado como root durante tempo suficiente para corrigir /config e iniciar corretamente os próprios serviços. Use user: quando a imagem documentar o suporte para o utilizador escolhido. A ausência de suporte para PUID, por si só, não significa que o entrypoint funcione sem root. Numa imagem linuxserver, definir user: torna o PUID e o 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 aspetos, por esta ordem. Primeiro, o próprio mount de media: o script de configuração de utilizadores da imagem base executa chown em /app, /config e /defaults, pelo que /data ou /downloads mantém a propriedade existente no host. Segundo, o grupo partilhado: atribua aos contentores que acedem à árvore de media o mesmo PGID e permissões de escrita para o grupo. Terceiro, o umask: uma máscara efetiva de 022 produz ficheiros 0644 quando a aplicação solicita 0666 e não se aplica nenhuma ACL predefinida. Assim, os outros contentores do grupo podem lê-los, mas não os podem modificar. Deixar UMASK por definir não garante uma máscara ou um modo de ficheiro específico. Defina UMASK=002 e ative o bit setgid nos diretórios com chmod 2775 para que os ficheiros novos herdem o grupo. Uma aplicação que solicite 0600 continua a criar ficheiros privados, e uma ACL predefinida altera a forma como as permissões de criação são atribuídas.

Este artigo foi útil?