El problema real#
Llevo meses ejecutando servicios en mi servidor doméstico con Docker Compose. Hace poco intenté configurar una contraseña con caracteres especiales en mi archivo .env. La contraseña era algo como Pass$word123!@. Al iniciar los contenedores, la variable llegaba vacía o malformada. Después de investigar, descubrí que Docker Compose interpretaba el $ como referencia a otra variable.
Por qué sucede: interpolación de variables#
Docker Compose interpreta el archivo .env de forma especial. Cuando encuentra un $ seguido de un nombre válido de variable, intenta sustituirlo por su valor. Si esa variable no existe, la deja vacía o genera un error silencioso.
Ejemplo del problema:
# .env
DB_PASSWORD=Pass$word123
API_KEY=sk_test_$random_key
SECRET=$UNDEFINED_VAREn estos casos, Docker Compose buscará variables llamadas word123, random_key y UNDEFINED_VAR. Obviamente no las encontrará, y tus valores quedarán corruptos.
La solución: comillas simples#
La solución más confiable es envolver los valores en comillas simples. Las comillas simples previenen la interpolación de variables en Docker Compose, exactamente como funcionan en bash.
# .env - FORMA CORRECTA
DB_PASSWORD='Pass$word123'
API_KEY='sk_test_$random_key'
SECRET='$UNDEFINED_VAR'
COMPLEX='!@#$%^&*()'Con comillas simples, Docker Compose trata todo el contenido como texto literal. No intenta resolver referencias a variables.
Usando las variables en docker-compose.yml#
Una vez definidas correctamente en .env, usarlas en tu docker-compose.yml es normal:
version: '3.8'
services:
database:
image: postgres:15
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USER}
api:
image: mi-api:latest
environment:
API_KEY: ${API_KEY}
DB_SECRET: ${SECRET}Docker Compose cargará los valores desde .env e inyectará las variables correctamente en los contenedores.
El segundo problema: restart no recarga variables#
Aquí viene lo frustrante. Después de modificar tu archivo .env, ejecutas:
docker-compose restartY descubres que los contenedores siguen usando los valores antiguos. Esto sucede porque restart solo reinicia los contenedores existentes sin recrearlos. No vuelve a leer el archivo .env.
La solución: –force-recreate#
Para que Docker Compose lea nuevamente el archivo .env y aplique las nuevas variables, debes recrear los contenedores. El comando correcto es:
docker-compose up -d --force-recreateO si prefieres una secuencia más explícita:
docker-compose down
docker-compose up -dLa opción --force-recreate fuerza la recreación incluso si la imagen no ha cambiado. Sin ella, Docker Compose podría reutilizar contenedores existentes.
Mi flujo de trabajo actual#
Después de experimentar con esto, así es como manejo las variables en mi servidor:
Defino todo en
.envcon comillas simples:MYSQL_ROOT_PASSWORD='R00t!$pecial#Pass' MYSQL_USER='admin' MYSQL_PASSWORD='Pass$word@123'Referencia en
docker-compose.yml:environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_PASSWORD: ${MYSQL_PASSWORD}Después de modificar
.env, siempre uso:docker-compose up -d --force-recreateVerifico que los cambios se aplicaron:
docker-compose exec servicio env | grep MI_VAR
Lecciones aprendidas#
- Los caracteres especiales
$,!,@,#en valores requieren comillas simples restartsolo reinicia, no recarga configuración--force-recreatees imprescindible tras modificar.env- Siempre verifica que las variables se hayan cargado correctamente dentro del contenedor
Estos detalles ahorraron muchas horas de debugging en mi setup doméstico. Espero que te ahorren también las tuyas.