A l’article anterior vam veure com definir serveis amb Docker Compose i com passar variables d’entorn directament al fitxer compose.yaml. Aquesta aproximació funciona per a exemples senzills, però quan fem feina amb aplicacions reals que requereixen credencials de bases de dades, claus API o configuracions específiques per entorn, necessitam una estratègia més robusta.
Incrustar credencials directament al fitxer de configuració és una pràctica perillosa: si versionam el fitxer amb Git, les credencials quedaran exposades a l’historial del repositori per sempre. A més, fa difícil reutilitzar la mateixa configuració en diferents entorns (desenvolupament, proves, producció) sense haver de modificar el fitxer cada vegada.
Docker Compose ofereix diverses eines per gestionar la configuració de manera segura i flexible: variables d’entorn en línia, fitxers .env, la directiva env_file i interpolació de variables. En aquest article explorarem cadascuna d’aquestes opcions i les seves aplicacions pràctiques.
Variables d’entorn inline #
La manera més directa de passar variables d’entorn a un contenidor és usant la clau environment dins la definició del servei. Aquesta clau accepta dos formats, mapa i llista, ambdós equivalents. Com que el format mapa, o diccionari, es més legible, usarem el format mapa per a tots els exemples d’aquesta sèrie.
Exemple:
services:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_USER: docmost
POSTGRES_PASSWORD: secretpassword
POSTGRES_DB: docmostLes variables definides amb
environments’injecten directament al contenidor i estan disponibles per al procés que s’hi executa.
Fitxers .env
#
Un fitxer .env és un fitxer de text pla que conté parells clau-valor, un per línia. Docker Compose carrega automàticament el fitxer .env que es trobi al mateix directori que el fitxer compose.yaml:
# Configuració de PostgreSQL
POSTGRES_USER=docmost
POSTGRES_PASSWORD=secretpassword
POSTGRES_DB=docmost
# Configuració de l'aplicació
APP_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
APP_URL=http://localhost:3000La sintaxi dels fitxers .env és senzilla:
- Cada línia conté una assignació
CLAU=valor - Les línies que comencen amb
#són comentaris - Els espais al voltant del
=no estan permesos - Els valors poden contenir espais si s’envolten amb cometes dobles
- Les línies buides s’ignoren
Interpolació de variables
Un cop definides les variables al fitxer .env, podem usar-les dins el compose.yaml amb la sintaxi ${VARIABLE}:
services:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}Quan executam docker compose up, Compose substitueix ${POSTGRES_USER} pel valor docmost definit al fitxer .env, i el mateix amb la resta de variables. Aquesta interpolació és especialment útil per:
- Compartir valors entre múltiples serveis.
- Mantenir les credencials fora del fitxer
compose.yaml. - Facilitar el canvi de configuració sense modificar el fitxer principal.
Valors per defecte
Podem especificar valors per defecte en cas que una variable no estigui definida:
services:
webapp:
image: myapp:latest
environment:
LOG_LEVEL: ${LOG_LEVEL:-info}
DEBUG: ${DEBUG:-false}La sintaxi ${VARIABLE:-valor_defecte} assigna valor_defecte si VARIABLE no existeix o està buida.
Especificar un fitxer .env diferent
Per defecte, Compose cerca un fitxer anomenat .env al mateix directori on es troba el fitxer compose.yaml, però podem especificar un fitxer diferent amb l’opció --env-file:
docker compose --env-file .env.production up --detachAixò és útil per mantenir configuracions separades per a diferents entorns:
projecte/
├── compose.yaml
├── .env # Desenvolupament (carregat per defecte)
├── .env.staging # Proves
└── .env.production # ProduccióDirectiva env_file
#
Mentre que el fitxer .env s’usa principalment per a la interpolació dins el compose.yaml, la directiva env_file carrega variables d’entorn directament dins el contenidor:
services:
webapp:
image: myapp:latest
env_file:
- .env.webappEl fitxer .env.webapp podria contenir:
DATABASE_URL=postgresql://user:pass@db:5432/myapp
REDIS_URL=redis://redis:6379
SECRET_KEY=mysupersecretkeyAquestes variables estaran disponibles dins el contenidor però no seran visibles ni interpolables dins el compose.yaml.
Diferència entre .env i env_file
És important entendre la diferència entre el fitxer .env i la directiva env_file:
| Característica | Fitxer .env |
Directiva env_file |
|---|---|---|
| Propòsit | Interpolació dins compose.yaml |
Injectar variables al contenidor |
| Carrega automàtica | Sí (si es diu .env) |
No, cal especificar-ho |
| Variables visibles a Compose | Sí | No |
| Variables disponibles al contenidor | Només si s’interpolen | Sí, totes |
Podem combinar ambdues tècniques:
services:
webapp:
image: myapp:${APP_VERSION} # Interpola des de .env
env_file:
- .env.webapp # Carrega al contenidor
environment:
APP_URL: ${APP_URL} # Interpola des de .envPrecedència de variables #
Quan una mateixa variable es defineix en múltiples llocs, Docker Compose segueix un ordre de precedència (de major a menor prioritat):
- Variables d’entorn del sistema operatiu, exportades amb
exporto com a part de la comanda executada. - Fitxer especificat amb
--env-file. - Fitxer
.enval directori actual. - Variables definides dins
env_filealcompose.yaml. - Variables definides dins
environmentalcompose.yaml.
Aquesta jerarquia permet sobreescriure valors sense modificar els fitxers de configuració. El següent exemple sobreescriu temporalment una variable d’entorn:
POSTGRES_PASSWORD=noupassword docker compose up --detachVariables predefinides #
Docker Compose predefineix algunes variables que podem usar per personalitzar el comportament:
| Variable | Descripció |
|---|---|
COMPOSE_PROJECT_NAME |
Nom del projecte (per defecte, el nom del directori) |
COMPOSE_FILE |
Ruta al fitxer de configuració |
COMPOSE_PROFILES |
Perfils actius (els veurem a un article posterior) |
DOCKER_HOST |
Socket de Docker a usar |
Per exemple, per executar el mateix projecte amb un nom diferent:
COMPOSE_PROJECT_NAME=docmost-prod docker compose up --detachExemple pràctic #
Posem en pràctica tot el que hem après amb un exemple real amb Docmost, PostgreSQL i Redis. Docmost és un wiki col·laboratiu de codi obert que requereix PostgreSQL, Redis i, opcionalment, emmagatzematge S3. Per simplificar, en aquest article usarem emmagatzematge local per als fitxers adjunts.
Estructura del projecte #
docmost/
├── compose.yaml
├── .env
├── .env.example
└── .gitignoreFitxer .env.example
#
El fitxer .env.example és un fitxer d’exemple que documentarà les variables necessàries sense contenir valors reals i que podem pujar al nostre repositori Git:
# Configuració de Docmost
APP_SECRET=genera_amb_openssl_rand_hex_32
APP_URL=http://localhost:3000
# Base de dades PostgreSQL
POSTGRES_USER=docmost
POSTGRES_PASSWORD=canvia_aquest_password
POSTGRES_DB=docmostFitxer .env
#
Copiam .env.example a .env i hi posam els valors reals:
# Configuració de Docmost
APP_SECRET=7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece
APP_URL=http://localhost:3000
# Base de dades PostgreSQL
POSTGRES_USER=docmost
POSTGRES_PASSWORD=Xk9mP2vL8nQ4wR7j
POSTGRES_DB=docmostPodem usar OpenSSL per a generar secret aleatoris. La següent comanda genera una cadena de caràcters alfanumèrics, eliminant /, = i + perquè són part de l’alfabet Base64:
openssl rand -base64 25 | tr --delete /=+ | cut --characters -32Fitxer .gitignore
#
Per evitar que les credencials es pugin al repositori Git en assegurarem de que el nostre fitxer .gitignore inclogui les següents línies:
.env
!.env.exampleFitxer compose.yaml
#
services:
docmost:
image: docmost/docmost:latest
container_name: docmost
depends_on:
- db
- redis
- garage
environment:
APP_URL: ${APP_URL}
APP_SECRET: ${APP_SECRET}
DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"
REDIS_URL: "redis://redis:6379"
ports:
- "3000:3000"
volumes:
- docmost-data:/app/data/storage
restart: unless-stopped
db:
image: postgres:18-alpine
container_name: docmost-db
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres-data:/var/lib/postgresql
restart: unless-stopped
redis:
image: redis:8-alpine
container_name: docmost-redis
command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
docmost-data:
postgres-data:
redis-data:Observa com:
- Totes les credencials s’interpolen des del fitxer
.env. - La variable
DATABASE_URLes construeix combinant múltiples variables. - Cada servei té el seu volum per a persistència.
Arrencar els serveis #
Cream el directori de feina:
mkdir --parents ~/Projects/docmost
cd ~/Projects/docmostDesprés de crear-hi a dins tots els fitxers especificats en els anteriors apartats, arrenquem els serveis:
docker compose up --detachBones pràctiques #
Per concloure, algunes recomanacions per gestionar la configuració de manera segura i mantenible:
Seguretat:
- Mai versionis fitxers
.envamb credencials reals. - Usa
.env.exampleper documentar les variables necessàries. - Genera secrets aleatoris amb
opensslo similar. - Afegeix
.enval.gitignoredel projecte.
Organització:
- Manté fitxers
.envseparats per entorn, e.g.,.env.production. - Documenta cada variable amb comentaris.
- Usa noms de variables descriptius i en majúscules.
Mantenibilitat:
- Centralitza les credencials compartides al fitxer
.env. - Evita duplicar valors; usa interpolació.
- Revisa periòdicament les credencials i rota-les si escau.
Exercicis #
Es proposen dos exercicis pràctics per facilitar l’aprenentatge progressiu.
Exercici 1 #
Metabase amb variables d’entorn
En aquest exercici es proposa desplegar Metabase, una eina de business intelligence, usant variables d’entorn per a la configuració. Passos:
- Crea un directori
metabaseal teu directori de projectes. - Crea un fitxer
.envamb les variables necessàries per a PostgreSQL. - Crea un fitxer
compose.yamlamb dos serveis:metabaseamb la imatgemetabase/metabase:v0.59.xal port 3000.dbamb la imatgepostgres:18-alpine.
- Configura Metabase per usar PostgreSQL com a base de dades d’aplicació (no la base de dades H2 per defecte). Les variables d’entorn de Metabase són:
MB_DB_TYPE: Tipus de base de dades (postgres).MB_DB_HOST: Nom del servei de PostgreSQL.MB_DB_PORT: Port de PostgreSQL (5432).MB_DB_DBNAME: Nom de la base de dades.MB_DB_USER: Usuari de la base de dades.MB_DB_PASS: Contrasenya de la base de dades.
- Arrenca els serveis i accedeix a
http://localhost:3000per completar la configuració inicial.
Consulta la documentació oficial de Metabase per a més informació sobre les variables d’entorn disponibles.
Respostes
Crea el directori de feina:
mkdir --parents ~/Projects/metabase
cd ~/Projects/metabaseCrea el fitxer .env:
# PostgreSQL
POSTGRES_USER=metabase
POSTGRES_PASSWORD=<password>
POSTGRES_DB=metabaseCrea el fitxer compose.yaml:
services:
metabase:
image: metabase/metabase:v0.59.x
container_name: metabase
depends_on:
- db
environment:
MB_DB_TYPE: postgres
MB_DB_HOST: db
MB_DB_PORT: 5432
MB_DB_DBNAME: ${POSTGRES_DB}
MB_DB_USER: ${POSTGRES_USER}
MB_DB_PASS: ${POSTGRES_PASSWORD}
ports:
- "3000:3000"
restart: unless-stopped
db:
image: postgres:18-alpine
container_name: metabase-db
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres-data:/var/lib/postgresql
restart: unless-stopped
volumes:
postgres-data:Executa Docker Compose:
docker compose up --detachAccedeix a http://localhost:3000 i segueix l’assistent de configuració inicial de Metabase.
Exercici 2 #
Vikunja amb env_file
En aquest exercici es proposa desplegar Vikunja, una aplicació de gestió de tasques, usant la directiva env_file per organitzar la configuració. Passos:
- Crea un directori
vikunjaal teu directori de projectes. - Crea dos fitxers de configuració:
.env.postgresamb les variables de PostgreSQL..env.vikunjaamb les variables de Vikunja.
- Crea un fitxer
compose.yamlamb dos serveis:vikunjaamb la imatgevikunja/vikunjaal port 3456.dbamb la imatgepostgres:18-alpine.
- Usa
env_fileper carregar les variables als contenidors corresponents. - Les variables de Vikunja més importants són:
VIKUNJA_SERVICE_PUBLICURL: URL pública de l’aplicació.VIKUNJA_SERVICE_JWTSECRET: Secret per als tokens JWT.VIKUNJA_DATABASE_TYPE: Tipus de base de dades (postgres).VIKUNJA_DATABASE_HOST: Nom del servei de PostgreSQL.VIKUNJA_DATABASE_USER: Usuari de la base de dades.VIKUNJA_DATABASE_PASSWORD: Contrasenya de la base de dades.VIKUNJA_DATABASE_DATABASE: Nom de la base de dades.
- Arrenca els serveis i accedeix a
http://localhost:3456per crear un compte.
Consulta la documentació oficial de Vikunja per a més informació sobre les opcions de configuració.
Respostes
Crea el directori de feina:
mkdir --parents ~/Projects/vikunja
cd ~/Projects/vikunjaCrea el fitxer .env.postgres:
POSTGRES_USER=vikunja
POSTGRES_PASSWORD=<password>
POSTGRES_DB=vikunjaCrea el fitxer .env.vikunja:
VIKUNJA_SERVICE_PUBLICURL=http://localhost:3456
VIKUNJA_SERVICE_JWTSECRET=<very-long-and-secure-secret>
VIKUNJA_DATABASE_TYPE=postgres
VIKUNJA_DATABASE_HOST=db
VIKUNJA_DATABASE_USER=vikunja
VIKUNJA_DATABASE_PASSWORD=<password>
VIKUNJA_DATABASE_DATABASE=vikunjaCrea el fitxer compose.yaml:
services:
vikunja:
image: vikunja/vikunja
container_name: vikunja
depends_on:
- db
env_file:
- .env.vikunja
ports:
- "3456:3456"
volumes:
- vikunja-files:/app/vikunja/files
restart: unless-stopped
db:
image: postgres:18-alpine
container_name: vikunja-db
env_file:
- .env.postgres
volumes:
- postgres-data:/var/lib/postgresql
restart: unless-stopped
volumes:
vikunja-files:
postgres-data:Executa Docker Compose:
docker compose up --detachAccedeix a http://localhost:3456 i crea un compte d’usuari per començar a usar Vikunja.