SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-10-13

Docker Compose: differenze tra .env, env_file e secrets

Scopri come .env, env_file e environment gestiscono le variabili in Docker Compose, quale valore prevale e perché le password devono stare nei secrets.

I tre elementi che vengono chiamati file env

Docker Compose dispone di tre meccanismi distinti, con nomi molto simili e facilmente confondibili. Il file .env sostituisce i segnaposto ${VARIABLE} direttamente in compose.yaml, dopo che Compose ha letto il file YAML e prima di eseguire la convalida o avviare qualsiasi operazione. L'attributo env_file: carica nel container un file di coppie chiave/valore come ambiente. L'attributo environment: imposta direttamente le variabili del container, definite nel file compose. I meccanismi non sono intercambiabili. Se due di essi impostano la stessa chiave, il valore viene determinato da un ordine di precedenza documentato.

Questa guida mostra ogni meccanismo in funzione e dimostra la precedenza con un comando eseguibile. Spiega quindi l'aspetto più importante: le variabili d'ambiente sono leggibili da chiunque possa eseguire docker inspect, quindi le password non devono essere inserite al loro interno. Se non hai mai utilizzato i file compose, inizia da Nozioni di base su Docker Compose su un VPS e torna qui per approfondire la configurazione.

Il file .env serve per il file Compose, non per il container

Create una directory e inserisci al suo interno due file.

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

Ora chiedi a Compose quali valori ha effettivamente analizzato.

docker compose config

L'output mostra image: alpine:3.20. Il segnaposto è scomparso perché l'interpolazione è stata eseguita quando Compose ha caricato il file. Compose cerca .env nella directory del progetto: nella directory specificata con --project-directory, se impostata; altrimenti nella directory del primo file passato con -f; in caso contrario nella directory corrente. Sostituisce i segnaposto ${NAME} presenti nei valori YAML. Le chiavi non vengono mai sottoposte a interpolazione e un doppio $$ produce un simbolo del dollaro letterale invece di una sostituzione.

Ora avvia il servizio.

docker compose run --rm demo

printenv ALPINE_TAG termina con lo stato 1 e non stampa nulla. La variabile non esiste all'interno del container. Questo è il malinteso più comune: .env ha configurato il file Compose, non il processo. Un file .env che contiene POSTGRES_PASSWORD=hunter2 non ha alcun effetto sul database, a meno che una parte del file Compose non vi faccia riferimento.

${NAME:-default} fornisce un valore predefinito quando la variabile non è impostata o è vuota. ${NAME:?message} impedisce a Compose di avviarsi e stampa il messaggio specificato; è la scelta corretta per un valore che non ha un valore predefinito sicuro.

env_file carica le variabili nel container

L'attributo env_file: indica uno o più file il cui contenuto diventa costituito da variabili d'ambiente del container.

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

Questo stampa from_env_file. Il formato del file consiste in righe di testo semplice KEY=value, una per riga; # all'inizio indica un commento. Non è sintassi shell, anche se Compose applica alcune regole simili a quelle della shell. Le virgolette esterne vengono rimosse, quindi KEY="value" e KEY=value producono lo stesso risultato. Nei valori racchiusi tra virgolette doppie e nei valori senza virgolette vengono sostituiti i segnaposto ${...}, mentre i valori racchiusi tra virgolette singole vengono usati senza modifiche. Il prefisso export è accettato, ma non è necessario. Gli spazi intorno al segno = vengono rimossi. Prestare attenzione ai commenti inline: in un valore senza virgolette, uno spazio seguito da # avvia un commento. Racchiudere quindi tra virgolette qualsiasi valore che contenga #.

Un percorso env_file mancante genera un errore e Compose si arresta. Contrassegnarlo come facoltativo se il file può legittimamente non essere presente:

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

environment imposta le variabili inline

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

Sono accettate due sintassi: la forma a mappa mostrata sopra e una forma a elenco che usa - GREETING=from_environment. Il comportamento è identico. Entrambe offrono un’ulteriore possibilità: una chiave senza valore trasferisce la variabile dalla shell in cui è stato eseguito docker compose. Nella forma a elenco si usa un - GREETING autonomo; nella forma a mappa si usa GREETING: senza alcun valore dopo i due punti.

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

Stampa from_my_shell. Se lo si esegue senza impostare GREETING nella shell, Compose non imposta alcun valore e non visualizza avvisi. È importante conoscere questi problemi di pass-through silenziosi: un servizio avviato con una variabile della password vuota spesso si avvia correttamente, ma resta semplicemente completamente esposto.

Quale valore prevale

Docker documenta l’ordine di precedenza, dal più alto al più basso: docker compose run -e nella riga di comando, quindi environment o env_file, il cui valore viene interpolato dalla shell o da un file env, quindi il semplice environment nel file Compose, poi env_file, infine la direttiva ENV incorporata nell’immagine.

In breve, per l’uso quotidiano: environment: prevale su env_file:, mentre -e nella riga di comando prevale su entrambi. Verificalo in un unico file.

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

Il primo comando stampa from_environment, quindi environment: ha sostituito il valore in app.env. Il secondo stampa from_cli. Nulla nel file Compose può sostituire il valore specificato nella riga di comando.

Quando un container si comporta come se la configurazione non fosse stata applicata, non procedere per tentativi. docker compose config stampa il file completamente risolto, mentre docker compose config --environment stampa le variabili usate da Compose per l’interpolazione. Nella maggior parte dei casi, i problemi segnalati come “il mio file env viene ignorato” dipendono da un valore impostato due volte a livelli diversi.

Perché le variabili d'ambiente vengono esposte

Impostare una password in environment: la salva nella configurazione del container su disco, dove è visibile a qualsiasi utente appartenente al gruppo docker.

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

L'output contiene "DB_PASSWORD=hunter2" in testo normale. Altri tre percorsi espongono lo stesso valore. docker compose config lo stampa nel terminale, ed è così che può finire incollato in un forum di supporto. Per impostazione predefinita, i processi figli ereditano la variabile, a meno che il processo padre non li avvii deliberatamente con un ambiente sostitutivo. Inoltre, un processo eseguito con lo stesso utente di PID 1, oppure in possesso di CAP_SYS_PTRACE, può leggerla da /proc/1/environ. In un container in cui tutto viene eseguito come root, questo vale per ogni processo. Un'applicazione eseguita come utente senza privilegi mentre PID 1 viene eseguito come root riceve invece un errore di autorizzazione su quel file, anche se mantiene la variabile nel proprio ambiente. Inoltre, i gestori degli arresti anomali delle applicazioni scaricano regolarmente l'intero ambiente in un log o in un report di errore.

L'appartenenza al gruppo docker equivale di fatto a disporre dei privilegi di root sull'host, quindi non è un confine di sicurezza su cui fare affidamento. La guida sugli account utente con privilegi minimi su un VPS spiega perché conviene limitare questo gruppo su qualsiasi server condiviso.

I secret di Compose mantengono il valore in un file

Compose supporta i secret basati su file. Il valore viene montato nel container come file invece di essere inserito nell'ambiente.

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

Il secret viene montato in /run/secrets/db_password all'interno del container. Il nome dopo la barra è il nome del secret definito nel blocco secrets: di primo livello.

Il suffisso _FILE è una convenzione usata dalle Docker Official Images, incluse postgres, mysql e mariadb. I relativi script entrypoint verificano la presenza di VARNAME_FILE, leggono il file e ne usano il contenuto. Non è una funzionalità di Docker, quindi funziona solo quando l'immagine la implementa. Consultare la documentazione dell'immagine prima di presumere che SOMETHING_FILE venga rispettato. Le applicazioni che non lo supportano spesso possono leggere direttamente il file all'avvio. In alternativa, è possibile passare il percorso e lasciare che sia il proprio entrypoint a gestirlo.

Verificare dall'interno del container in esecuzione:

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

Il primo comando stampa la password. Il secondo non stampa nulla, perché il valore non è mai entrato nell'ambiente. Questo è lo scopo: docker inspect su questo container mostra solo il percorso, che non contiene informazioni sensibili.

Proteggere il file sorgente sull'host, perché il secret è riservato solo quanto il file che lo contiene:

chmod 600 db_password.txt

Il compromesso pratico su un VPS

Molte immagini self-hosted non supportano le variabili _FILE, quindi le variabili d’ambiente sono l’unico modo per fornire questi valori. Su un VPS gestito da un solo amministratore, l’obiettivo realistico è impedire che i valori restino in un file leggibile da tutti nella directory del progetto e tenerli fuori da 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 crea il file con i permessi già impostati, quindi non esiste un intervallo in cui il file sia leggibile da chiunque. Il file appartiene a root, quindi un utente non root del server non può leggerlo. Tuttavia, chiunque possa eseguire docker può comunque leggere il valore dal container. Aggiungi *.env e .env a .gitignore e sottoponi al commit un file app.env.example che contenga invece i nomi delle chiavi con valori vuoti. Una password sottoposta al commit è una password da ruotare.

Ruotare un valore significa riavviare il servizio. Le variabili d’ambiente vengono lette una sola volta, quando viene avviato il processo del container. Modificare il file non produce quindi alcun effetto finché non esegui docker compose up -d --force-recreate db. È lo stesso schema usato nella guida n8n dietro HTTPS su un VPS, in cui la chiave di crittografia si trova all’esterno del file compose.

Suddividere la configurazione per ambiente

Compose legge .env dalla directory del progetto per impostazione predefinita. Per indicare un percorso diverso, usare --env-file.

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

I file multipli vengono letti nell'ordine indicato e quelli successivi sovrascrivono quelli precedenti. Mantenere i valori predefiniti non riservati in un file sottoposto a commit e i secret in un file che non deve mai lasciare il server. Lo stesso vale per env_file:: in caso di chiave duplicata, prevale quella dell'ultimo file elencato.

FAQ

Perché il file .env viene ignorato all'interno del container?

Non viene ignorato. Il file .env sostituisce i segnaposto ${NAME} nel file Compose e può anche impostare variabili proprie di Compose, come COMPOSE_PROJECT_NAME o COMPOSE_FILE. Non imposta mai variabili all'interno di un container. Per trasferire il valore nel container, è necessario referenziarlo: environment: { KEY: "${NAME}" }, oppure usare env_file: ./that-file.env.

environment ha la precedenza su env_file, o viceversa?

Prevale environment:. L'ordine documentato da Docker colloca l'attributo environment sopra l'attributo env_file; entrambi sono inferiori a docker compose run -e sulla riga di comando. Se una chiave è impostata in entrambi i punti, il valore in env_file viene ignorato senza messaggi.

Come posso visualizzare il valore finale usato da Compose?

Eseguire docker compose config per stampare il file Compose completamente risolto, con tutta l'interpolazione applicata. Per un container già in esecuzione, docker inspect <container> --format '{{json .Config.Env}}' mostra esattamente ciò che il relativo processo ha ricevuto.

I secret di Compose sono crittografati?

No. Un secret basato su file viene montato nel container come file in chiaro in /run/secrets/<name> e il file sorgente risiede sul disco dell'host senza crittografia. Il vantaggio riguarda l'ambito, non la crittografia: il valore resta fuori dall'ambiente del container, fuori dall'output di docker inspect e fuori dai crash dump che stampano l'ambiente.

Posso usare virgolette e spazi in un file env?

Sì. Compose rimuove le virgolette esterne, quindi KEY="value with spaces" e KEY=value with spaces producono entrambi value with spaces. Usare gli apici singoli quando il valore contiene un $ che non deve essere interpolato e racchiudere tra virgolette qualsiasi valore contenente #, perché senza virgolette quest'ultimo avvia un commento. Gli spazi attorno a = vengono rimossi, quindi KEY = value imposta comunque KEY.

Questo articolo ti è stato utile?