SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-10-11

Docker Compose: .env, env_file e environment

Entenda a diferença entre .env, env_file e environment no Docker Compose, a precedência entre valores e por que senhas não devem ficar em variáveis do contentor.

Os três mecanismos que as pessoas chamam de ficheiro de ambiente

O Docker Compose tem três mecanismos distintos com nomes muito semelhantes. O ficheiro .env preenche os marcadores ${VARIABLE} dentro do próprio compose.yaml, depois de o Compose ler o YAML e antes de validar ou executar qualquer ação. O atributo env_file: carrega um ficheiro de pares chave/valor para o ambiente do contentor. O atributo environment: define variáveis diretamente no contentor, no ficheiro Compose. Estes mecanismos não são intercambiáveis. Quando dois deles definem a mesma chave, o resultado é determinado por uma ordem de precedência documentada.

Este guia mostra cada mecanismo em funcionamento e comprova a precedência com um comando que pode executar. Depois, aborda a parte mais importante: as variáveis de ambiente podem ser lidas por qualquer pessoa que consiga executar docker inspect. Por isso, as palavras-passe não devem ser guardadas nelas. Se ainda não conhece os ficheiros Compose em geral, comece por Conceitos básicos do Docker Compose num VPS e volte aqui para consultar esta explicação.

O ficheiro .env é usado pelo ficheiro Compose, não pelo contentor

Crie um diretório e coloque nele dois ficheiros.

mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .env
services:
  demo:
    image: alpine:${ALPINE_TAG}
    command: printenv ALPINE_TAG

Agora peça ao Compose para mostrar o que interpretou efetivamente.

docker compose config

O resultado mostra image: alpine:3.20. O marcador foi removido porque a interpolação ocorreu quando o Compose carregou o ficheiro. O Compose procura .env no diretório do projeto: o diretório indicado com --project-directory, se o definir; caso contrário, o diretório do primeiro ficheiro passado com -f; caso contrário, o diretório atual. Substitui os marcadores ${NAME} que encontra nos valores YAML. As chaves nunca são interpoladas, e $$ duplicado produz um cifrão literal em vez de uma substituição.

Agora inicie o serviço.

docker compose run --rm demo

printenv ALPINE_TAG termina com o estado 1 e não apresenta qualquer saída. A variável não existe dentro do contentor. Este é o erro de interpretação mais comum: .env configurou o ficheiro Compose, não o processo. Um ficheiro .env que contenha POSTGRES_PASSWORD=hunter2 não tem qualquer efeito na base de dados, a menos que alguma parte do ficheiro Compose o referencie.

${NAME:-default} fornece um valor alternativo quando a variável não está definida ou está vazia. ${NAME:?message} faz o Compose recusar o arranque e apresentar a sua mensagem. Esta é a escolha correta para um valor que não tem um valor predefinido seguro.

env_file carrega variáveis no contentor

O atributo env_file: indica um ou mais ficheiros cujo conteúdo é carregado como variáveis de ambiente do contentor.

printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.env
services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
docker compose run --rm demo

Isto imprime from_env_file. O formato do ficheiro usa linhas simples de KEY=value, uma por linha, e # no início indica um comentário. Não é um script de shell, embora o Compose analise o ficheiro com algumas regras semelhantes às do shell. As aspas envolventes são removidas, por isso KEY="value" e KEY=value produzem o mesmo resultado. Os valores entre aspas duplas e sem aspas têm os marcadores ${...} interpolados, enquanto os valores entre aspas simples são usados literalmente. O prefixo export é aceite, mas não é necessário. Os espaços em redor do sinal = são removidos. Tenha atenção aos comentários na mesma linha: num valor sem aspas, um espaço seguido de # inicia um comentário. Por isso, coloque entre aspas qualquer valor que contenha #.

Um caminho env_file inexistente causa um erro e o Compose para. Marque o ficheiro como opcional se ele puder legitimamente não existir:

    env_file:
      - path: ./app.env
        required: false

environment define variáveis inline

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    environment:
      GREETING: from_environment

São aceites duas sintaxes: o formato de mapeamento acima e um formato de lista que usa - GREETING=from_environment. O comportamento é idêntico. Ambas têm um recurso adicional: uma chave sem valor transfere a variável do shell onde executou docker compose. No formato de lista, trata-se de um - GREETING isolado. No formato de mapeamento, é GREETING: sem nada depois dos dois-pontos.

    environment:
      - GREETING
GREETING=from_my_shell docker compose run --rm demo

Isto imprime from_my_shell. Execute o comando sem definir GREETING no shell e o Compose não define nada, sem apresentar qualquer aviso. É importante conhecer as falhas silenciosas de transferência, porque um serviço que arranca com uma variável de palavra-passe vazia geralmente inicia sem problemas e fica simplesmente totalmente exposto.

Qual valor prevalece

O Docker documenta a ordem de precedência, da mais alta para a mais baixa: docker compose run -e na linha de comandos, depois environment ou env_file, cujo valor é interpolado a partir da shell ou de um ficheiro de ambiente, depois environment simples no ficheiro Compose, depois env_file e, por fim, a diretiva ENV incorporada na imagem.

A versão curta para o trabalho diário: environment: prevalece sobre env_file:, e -e na linha de comandos prevalece sobre ambos. Comprove isso num único ficheiro.

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
    environment:
      GREETING: from_environment
docker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING

O primeiro comando imprime from_environment, porque environment: substituiu o valor em app.env. O segundo imprime from_cli. Nada no ficheiro Compose substitui a linha de comandos.

Quando um contentor se comporta como se a sua configuração nunca tivesse sido aplicada, não tente adivinhar. docker compose config imprime o ficheiro totalmente resolvido, e docker compose config --environment imprime as variáveis de interpolação que o Compose está a utilizar. A maioria dos relatos de que “o meu ficheiro de ambiente é ignorado” resulta de um valor definido duas vezes, em níveis diferentes.

Por que as variáveis de ambiente vazam

Defina uma palavra-passe em environment: e ela será armazenada na configuração do contentor em disco, ficando visível para qualquer utilizador do grupo docker.

docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'

A saída contém "DB_PASSWORD=hunter2" em texto simples. Mais três caminhos expõem o mesmo valor. docker compose config imprime-o no terminal, que é como acaba colado num fórum de suporte. Os processos filhos herdam a variável por predefinição, a menos que o processo-pai os inicie deliberadamente com um ambiente de substituição. Um processo executado como o mesmo utilizador que o PID 1, ou que tenha CAP_SYS_PTRACE, pode lê-la a partir de /proc/1/environ. Num contentor onde tudo é executado como root, isso aplica-se a todos os processos. Uma aplicação executada como utilizador sem privilégios enquanto o PID 1 é executado como root recebe um erro de permissões nesse ficheiro, mas continua a manter a variável no seu próprio ambiente. Além disso, os gestores de falhas das aplicações despejam habitualmente todo o ambiente num log ou num relatório de erro.

A pertença ao grupo docker equivale efetivamente a root no host. Portanto, não pode tratar isto como um limite de privilégios. O guia sobre contas de utilizador com privilégios mínimos numa VPS explica por que motivo vale a pena restringir esse grupo em qualquer servidor partilhado.

Os secrets do Compose mantêm o valor num ficheiro

O Compose suporta secrets baseados em ficheiros. O valor é montado no contentor como um ficheiro, em vez de ser injetado no ambiente.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt

O secret é montado em /run/secrets/db_password dentro do contentor. O nome depois da barra é o nome do secret definido no bloco de nível superior secrets:.

O sufixo _FILE é uma convenção usada pelas Docker Official Images, incluindo postgres, mysql e mariadb. Esses scripts de entrypoint verificam a existência de VARNAME_FILE, leem o ficheiro e usam o seu conteúdo. Isto não é uma funcionalidade do Docker. Só funciona quando a imagem a implementa. Consulte a documentação da imagem antes de assumir que SOMETHING_FILE será respeitado. As aplicações que não suportam esta convenção podem muitas vezes ler o ficheiro durante o arranque. Em alternativa, pode passar o caminho e deixar que o seu próprio entrypoint faça essa leitura.

Verifique a partir do contentor em execução:

docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORD

O primeiro comando imprime a palavra-passe. O segundo não imprime nada, porque o valor nunca entrou no ambiente. Esse é o objetivo: docker inspect neste contentor mostra apenas o caminho inofensivo.

Proteja o ficheiro de origem no host, porque o secret só é privado enquanto o ficheiro que o contém também o for:

chmod 600 db_password.txt

O meio-termo pragmático numa VPS

Muitas imagens self-hosted não suportam variáveis _FILE, por isso as variáveis de ambiente são a única forma de fornecer estes valores. Numa VPS administrada por uma única pessoa, o objetivo realista é impedir que os valores fiquem num ficheiro legível por todos no diretório do projeto e mantê-los fora do git.

sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env
    env_file:
      - /etc/myapp/app.env

install -m 600 cria o ficheiro com as permissões já definidas, por isso não existe um intervalo em que fique legível por todos. O root é o proprietário, portanto um utilizador que não seja root no servidor não o consegue ler, embora qualquer pessoa que consiga executar docker ainda possa obter o valor a partir do contentor. Adicione *.env e .env a .gitignore e faça commit de um app.env.example que contenha os nomes das chaves com valores vazios. Uma palavra-passe incluída num commit é uma palavra-passe que deve ser substituída.

A rotação de um valor implica reiniciar o serviço. As variáveis de ambiente são lidas uma vez, quando o processo do contentor arranca, por isso editar o ficheiro não altera nada até executar docker compose up -d --force-recreate db. Este é o mesmo padrão utilizado no guia n8n atrás de HTTPS numa VPS, em que a chave de encriptação fica fora do ficheiro compose.

Dividir a configuração por ambiente

O Compose lê .env do diretório do projeto por padrão. Aponte-o para outro local com --env-file.

docker compose --env-file .env.staging config

Vários ficheiros são lidos pela ordem indicada, e os ficheiros posteriores substituem os anteriores. Mantenha os valores predefinidos não secretos num ficheiro versionado e os segredos num ficheiro que nunca saia do servidor. O mesmo se aplica a env_file:: para uma chave duplicada, prevalece o último ficheiro listado.

FAQ

Por que o meu ficheiro .env é ignorado dentro do contentor?

Ele não é ignorado. O ficheiro .env substitui os marcadores ${NAME} no ficheiro Compose e também pode definir variáveis do próprio Compose, como COMPOSE_PROJECT_NAME ou COMPOSE_FILE. Ele nunca define variáveis dentro de um contentor. Para passar o valor para o contentor, faça referência a ele: environment: { KEY: "${NAME}" }, ou use env_file: ./that-file.env.

environment substitui env_file ou acontece o contrário?

environment: tem precedência. A ordem documentada pelo Docker coloca o atributo environment acima do atributo env_file, e ambos ficam abaixo de docker compose run -e na linha de comandos. Se uma chave estiver definida nos dois locais, o valor em env_file não é utilizado e não é apresentado nenhum aviso.

Como vejo o valor final que o Compose vai utilizar?

Execute docker compose config para imprimir o ficheiro Compose totalmente resolvido, com toda a interpolação aplicada. Para um contentor que já está em execução, docker inspect <container> --format '{{json .Config.Env}}' mostra exatamente o que o processo recebeu.

Os secrets do Compose são encriptados?

Não. Um secret baseado num ficheiro é montado no contentor como um ficheiro simples em /run/secrets/<name>, e o ficheiro de origem fica no disco do host sem encriptação. A vantagem é o âmbito, não a encriptação: o valor não aparece no ambiente do contentor, no resultado de docker inspect nem em dumps de falhas que imprimam o ambiente.

Posso usar aspas e espaços num ficheiro env?

Sim. O Compose remove as aspas exteriores, por isso KEY="value with spaces" e KEY=value with spaces produzem ambos value with spaces. Use aspas simples quando o valor contiver um $ que não deve ser interpolado e coloque entre aspas qualquer valor que contenha #, porque, sem aspas, isso inicia um comentário. Os espaços à volta de = são removidos, por isso KEY = value ainda define KEY.

Este artigo foi útil?