Las variables de entorno mantienen los secretos fuera del código fuente y permiten cambiar la configuración sin redesplegar. El archivo .env se ha convertido en la forma estándar de gestionarlas localmente. Esta guía cubre reglas de sintaxis, configuración de frameworks, seguridad, archivos específicos por entorno, integración con Docker, errores comunes y alternativas en producción.
Reglas de sintaxis .env
Un archivo .env es un archivo de texto plano con una variable por línea. Estas son las reglas que todo desarrollador debe conocer:
Sintaxis básica
Cada línea sigue el patrón KEY=VALUE. Sin espacios alrededor del signo igual.
# .env
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=sk-1234567890abcdef
PORT=3000
DEBUG=trueReglas de comillas
Los valores pueden ir sin comillas, con comillas simples o dobles. El comportamiento difiere:
# Unquoted — trailing whitespace trimmed
APP_NAME=My Application
# Single quotes — literal, no interpolation
PASSWORD='p@$$w0rd#123'
# Double quotes — escape sequences + interpolation
GREETING="Hello\nWorld"
FULL_URL="${BASE_URL}/api/v1"- Sin comillas: se eliminan espacios finales, sin secuencias de escape.
- Comillas simples: el valor se toma literalmente, sin interpolación ni escape.
- Comillas dobles: soporta secuencias de escape (\n, \t) e interpolación de variables.
Valores multilínea
Use comillas dobles y \n para saltos de línea, o saltos de línea reales dentro de comillas dobles:
# Method 1: \n in double quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIB...\n-----END RSA PRIVATE KEY-----"
# Method 2: actual newlines in double quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"Comentarios
Las líneas que comienzan con # son comentarios. Los comentarios en línea funcionan solo con valores sin comillas en algunos parsers:
# This is a full-line comment
DATABASE_URL=postgres://localhost/mydb # inline comment (some parsers)
# Empty lines are ignored
API_KEY=abc123 # This may or may not work depending on the parserInterpolación de variables
Referencia otras variables usando la sintaxis ${VAR} (soportado por la mayoría de parsers en valores con comillas dobles):
BASE_URL=https://api.example.com
API_V1=${BASE_URL}/v1
API_V2=${BASE_URL}/v2
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/${DB_NAME}Configuración de frameworks
Node.js (dotenv)
El cargador .env más popular para Node.js. Instalación y configuración:
# Install
npm install dotenv
# --- app.js (CommonJS) ---
require('dotenv').config();
console.log(process.env.DATABASE_URL);
# --- app.ts (ES Modules) ---
import 'dotenv/config';
console.log(process.env.DATABASE_URL);
# --- Or load from CLI ---
node -r dotenv/config app.js
node -r dotenv/config app.js dotenv_config_path=.env.localNext.js, Vite y Create React App cargan archivos .env automáticamente — no se necesita paquete.
Python (python-dotenv)
Cargar .env en proyectos Python:
# Install
pip install python-dotenv
# --- app.py ---
from dotenv import load_dotenv
import os
load_dotenv() # loads .env from current directory
# load_dotenv('.env.local') # or specify a path
database_url = os.getenv('DATABASE_URL')
print(database_url)
# --- Django: manage.py or settings.py ---
from dotenv import load_dotenv
from pathlib import Path
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)Go (godotenv)
Cargar .env en proyectos Go:
// Install
// go get github.com/joho/godotenv
package main
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
)
func main() {
err := godotenv.Load() // loads .env
if err != nil {
log.Fatal("Error loading .env file")
}
dbURL := os.Getenv("DATABASE_URL")
fmt.Println(dbURL)
}PHP (vlucas/phpdotenv)
Usado por Laravel y la mayoría de frameworks PHP:
// Install
// composer require vlucas/phpdotenv
<?php
require 'vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Access variables
$dbUrl = $_ENV['DATABASE_URL'];
// or
$dbUrl = getenv('DATABASE_URL');
// Required variables (throws exception if missing)
$dotenv->required(['DATABASE_URL', 'API_KEY']);Ruby (gem dotenv)
Usado por Rails y otros proyectos Ruby:
# Gemfile
gem 'dotenv-rails', groups: [:development, :test]
# Or for non-Rails Ruby:
gem 'dotenv'
# --- app.rb ---
require 'dotenv/load'
puts ENV['DATABASE_URL']
# --- Rails: .env is loaded automatically ---
# Access via ENV['DATABASE_URL'] anywhere in your appSeguridad: nunca hagas commit de archivos .env
Tu archivo .env contiene secretos (claves API, contraseñas de base de datos, tokens). Hacer commit al control de versiones es el error de seguridad n.°1.
Patrones .gitignore
Agrega estos patrones a tu archivo .gitignore inmediatamente:
# .gitignore
# Ignore all .env files
.env
.env.local
.env.*.local
.env.development
.env.production
# More aggressive pattern — ignore all .env variants
.env*
# But DO commit the example file
!.env.exampleUsar .env.example
Haz commit de un archivo .env.example con valores vacíos para que los compañeros sepan qué variables se necesitan:
# .env.example — commit this file
# Copy to .env and fill in your values
DATABASE_URL=
API_KEY=
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
REDIS_URL=
JWT_SECRET=
# Optional
DEBUG=false
LOG_LEVEL=info¿Ya hiciste commit del .env?
Si accidentalmente hiciste commit de un archivo .env, elimínalo del seguimiento y rota todos los secretos:
# Step 1: Remove .env from Git tracking (keeps local file)
git rm --cached .env
# Step 2: Add to .gitignore
echo ".env" >> .gitignore
# Step 3: Commit the removal
git add .gitignore
git commit -m "Remove .env from tracking, add to .gitignore"
# Step 4: CRITICAL — Rotate ALL secrets in the .env file
# Every API key, password, and token that was exposed
# must be regenerated immediatelyArchivos específicos por entorno
La mayoría de frameworks soportan múltiples archivos .env para diferentes entornos. Entender el orden de carga es crítico.
Orden de carga (Next.js / Vite / CRA)
Los archivos se cargan en esta prioridad (los archivos posteriores sobrescriben a los anteriores):
# Loading priority (highest to lowest):
# 1. Shell environment variables (always win)
# 2. .env.{NODE_ENV}.local (e.g. .env.production.local)
# 3. .env.local (NOT loaded in test)
# 4. .env.{NODE_ENV} (e.g. .env.production)
# 5. .env (default fallback)
# Example for NODE_ENV=production:
# .env → loaded first (base defaults)
# .env.production → overrides .env
# .env.local → overrides .env.production
# .env.production.local → overrides everything above.env.local siempre es ignorado por git (agrégalo a .gitignore). NO se carga durante las pruebas para mantener los tests deterministas.
¿Qué archivo para qué?
| File | Purpose | Git |
|---|---|---|
| .env | Default values, non-secret config | Commit |
| .env.example | Template with empty values | Commit |
| .env.local | Local secrets & overrides | Ignore |
| .env.development | Dev-specific (shared) | Commit |
| .env.production | Production-specific (non-secret) | Commit |
| .env.test | Test environment config | Commit |
| .env.production.local | Production secrets (local only) | Ignore |
Integración con Docker y Docker Compose
Directiva env_file
Docker Compose puede cargar archivos .env directamente en contenedores:
# docker-compose.yml (or compose.yml)
services:
web:
image: node:20-alpine
env_file:
- .env # base defaults
- .env.production # production overrides
ports:
- "3000:3000"
db:
image: postgres:16
env_file:
- .env.db # separate file for DB secrets
volumes:
- pgdata:/var/lib/postgresql/dataenvironment vs env_file
También puedes definir variables de entorno en línea. Esta es la diferencia:
# Using env_file (loads from file)
services:
web:
env_file:
- .env
# Using environment (inline)
services:
web:
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=${DATABASE_URL} # from shell or .env
# Using environment (mapping syntax)
services:
web:
environment:
NODE_ENV: production
PORT: 3000- env_file: carga desde archivo, mantiene compose.yml limpio, fácil de cambiar por entorno.
- environment: visible en compose.yml, bueno para valores no secretos, soporta sustitución de variables.
.env para variables de Docker Compose
Docker Compose lee automáticamente un archivo .env en la raíz del proyecto para sustitución de variables en compose.yml:
# .env (in same directory as compose.yml)
POSTGRES_VERSION=16
NODE_VERSION=20
APP_PORT=3000
# compose.yml — uses .env for variable substitution
services:
web:
image: node:${NODE_VERSION}-alpine
ports:
- "${APP_PORT}:3000"
db:
image: postgres:${POSTGRES_VERSION}
environment:
POSTGRES_DB: myapp10 errores comunes de .env y soluciones
Estos son los problemas .env más frecuentes que encuentran los desarrolladores:
| # | Error | Causa | Solución |
|---|---|---|---|
| 1 | process.env.VAR es undefined | dotenv no cargado o cargado después del uso | Llama a dotenv.config() al inicio del archivo de entrada |
| 2 | Variables vacías en producción | Archivo .env no desplegado; dependencia del archivo en vez de variables de plataforma | Configura variables de entorno en tu plataforma de hosting (Vercel, AWS, etc.) |
| 3 | Valores .env contienen comillas | El parser trata las comillas como caracteres literales | Revisa la documentación del parser |
| 4 | Archivo .env incorrecto cargado | El directorio de trabajo difiere de la ruta esperada | Usa la opción path: dotenv.config({ path: ".env.local" }) |
| 5 | Valor multilínea truncado | Valor no correctamente entre comillas dobles | Envuelve valores multilínea en comillas dobles |
| 6 | Caracteres especiales rompen el valor | $ o # interpretado como interpolación/comentario | Usa comillas simples para prevenir interpolación, o escapa con \ |
| 7 | Error de codificación BOM | Editor de Windows guarda .env con UTF-8 BOM | Guarda como UTF-8 sin BOM |
| 8 | Variables de entorno del contenedor Docker vacías | Ruta de env_file incorrecta o archivo fuera del contexto de build | Verifica que la ruta sea relativa a la ubicación de compose.yml |
| 9 | Espacios alrededor de = rompen el parsing | KEY = VALUE en vez de KEY=VALUE | Elimina espacios alrededor del signo igual |
| 10 | Variables no disponibles en el navegador (React/Next) | Falta el prefijo requerido (NEXT_PUBLIC_ o REACT_APP_) | Agrega el prefijo requerido por el framework para exponer al código del cliente |
Alternativas en producción
En producción, los archivos .env no son recomendados. Usa estas alternativas:
Variables de entorno de plataforma
Cada plataforma de hosting importante proporciona UI o CLI para configurar variables de entorno:
# Vercel
vercel env add DATABASE_URL production
vercel env ls
# AWS (Parameter Store)
aws ssm put-parameter \
--name "/myapp/prod/DATABASE_URL" \
--value "postgres://..." \
--type SecureString
# Heroku
heroku config:set DATABASE_URL=postgres://...
heroku config
# Railway
railway variables set DATABASE_URL=postgres://...
# Fly.io
fly secrets set DATABASE_URL=postgres://...Docker Secrets
Para Docker Swarm o Compose, usa secrets para datos sensibles:
# compose.yml with Docker secrets
services:
web:
image: myapp:latest
secrets:
- db_password
- api_key
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt # for Compose
# external: true # for Swarm
api_key:
file: ./secrets/api_key.txt
# In your app, read from file:
# const secret = fs.readFileSync('/run/secrets/db_password', 'utf8').trim();Gestores de secretos (Vault, AWS Secrets Manager)
Para aplicaciones empresariales, usa un gestor de secretos dedicado:
# HashiCorp Vault
vault kv put secret/myapp/production \
DATABASE_URL="postgres://..." \
API_KEY="sk-..."
# Read in application
vault kv get -field=DATABASE_URL secret/myapp/production
# AWS Secrets Manager
aws secretsmanager create-secret \
--name "myapp/production/db" \
--secret-string '{"url":"postgres://...","password":"..."}'
# Read in Node.js with AWS SDK
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManager({ region: 'us-east-1' });
const { SecretString } = await client.getSecretValue({
SecretId: 'myapp/production/db'
});
const secrets = JSON.parse(SecretString);Los gestores de secretos proporcionan rotación, logs de auditoría, control de acceso y cifrado en reposo — funciones que los archivos .env no pueden ofrecer.
Preguntas frecuentes
¿Debo hacer commit de mi archivo .env en Git?
Nunca hagas commit de archivos .env con secretos al control de versiones. En su lugar, haz commit de un archivo .env.example con valores vacíos. Agrega patrones .env* a tu .gitignore.
¿Cuál es la diferencia entre .env, .env.local y .env.production?
.env contiene valores por defecto cargados en todos los entornos. .env.local contiene sobrecargas locales no committeadas. .env.production contiene valores específicos de producción cargados solo cuando NODE_ENV=production.
¿Por qué process.env.MY_VAR es undefined en Node.js?
Causas más comunes: 1) Olvidar llamar require("dotenv").config() al inicio, 2) El archivo .env no está en la raíz, 3) Error tipográfico en el nombre de variable, 4) Con ES modules, usar "import dotenv/config".
¿Cómo uso variables .env en Docker Compose?
Docker Compose lee automáticamente un archivo .env en el mismo directorio que compose.yml para sustitución de variables ${VAR}. Usa env_file o environment para pasar variables a contenedores.
¿Se pueden usar archivos .env en producción?
No es recomendado. En producción, usa variables de entorno de plataforma, Docker secrets o un gestor de secretos como HashiCorp Vault o AWS Secrets Manager.
¿Cómo expongo variables .env al navegador en React o Next.js?
En Next.js, prefija con NEXT_PUBLIC_. En Create React App, usa REACT_APP_. En Vite, usa VITE_. Solo las variables con prefijo se incluyen en el bundle del cliente.