Mira la lección en vídeo
Accede al vídeo completo de esta lección y a más contenido exclusivo con el Plan Plus.
Desbloquear Plan PlusDockerfile
Un Dockerfile es un archivo de texto que contiene una serie de instrucciones que Docker ejecuta automáticamente para construir una imagen. Este archivo define paso a paso cómo debe configurarse el entorno, qué software instalar, qué archivos copiar y cómo debe ejecutarse la aplicación dentro del contenedor.
Anatomía básica de un Dockerfile
La estructura fundamental de un Dockerfile sigue un patrón específico donde cada línea representa una instrucción que crea una nueva capa en la imagen:
# Comentarios empiezan con #
FROM ubuntu:25.10
# Instalar dependencias
RUN apt-get update && apt-get install -y \
python3 \
python3-pip
# Establecer directorio de trabajo
WORKDIR /app
# Copiar archivos
COPY requirements.txt .
RUN pip3 install -r requirements.txt
# Copiar código fuente
COPY . .
# Exponer puerto
EXPOSE 8000
# Comando por defecto
CMD ["python3", "app.py"]
Principios de ordenación de instrucciones
El orden de las instrucciones en un Dockerfile es crucial debido al sistema de capas de Docker. Cada instrucción crea una nueva capa, y Docker utiliza un mecanismo de caché que reutiliza capas que no han cambiado.
Regla fundamental: las instrucciones que cambian con menos frecuencia deben colocarse al principio, mientras que las que cambian más a menudo van al final.
# ✅ Buena estructura - de menos a más cambiante
FROM node:24-alpine
# Instalar dependencias del sistema (raramente cambia)
RUN apk add --no-cache git
# Establecer directorio de trabajo
WORKDIR /usr/src/app
# Copiar solo package.json primero (cambia menos que el código)
COPY package*.json ./
# Instalar dependencias de Node.js
RUN npm ci --only=production
# Copiar código fuente (cambia frecuentemente)
COPY . .
# Exponer puerto y ejecutar
EXPOSE 3000
CMD ["npm", "start"]
Concepto de capas y invalidación de caché
Cada instrucción en un Dockerfile genera una capa inmutable. Cuando modificas una instrucción, Docker invalida esa capa y todas las posteriores, obligando a reconstruirlas.
Ejemplo de invalidación ineficiente:
# ❌ Estructura ineficiente
FROM python:3.13
# Si cambia el código, se invalida todo lo siguiente
COPY . /app
WORKDIR /app
# Estas instalaciones se ejecutarán cada vez que cambie el código
RUN pip install flask
RUN pip install requests
Ejemplo de estructura optimizada:
# ✅ Estructura optimizada
FROM python:3.13
WORKDIR /app
# Copiar solo requirements.txt primero
COPY requirements.txt .
# Instalar dependencias (solo se ejecuta si cambia requirements.txt)
RUN pip install -r requirements.txt
# Copiar código al final
COPY . .
Comentarios y documentación
Los comentarios en Dockerfile utilizan el símbolo #
y son fundamentales para documentar la intención de cada paso:
# Usar imagen base oficial de Node.js con Alpine Linux
FROM node:24-alpine
# Crear usuario no-root por seguridad
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# Instalar dependencias del sistema necesarias para la compilación
RUN apk add --no-cache libc6-compat
# Establecer directorio de trabajo en el contenedor
WORKDIR /app
# Copiar archivos de configuración de dependencias
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
Buenas prácticas estructurales básicas
Aprovechar el paralelismo de BuildKit ordenando instrucciones independientes:
FROM nginx:1.29.0-alpine
# Estas dos instrucciones pueden ejecutarse en paralelo
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/dist /usr/share/nginx/html
# Configuración que depende de los archivos anteriores
RUN nginx -t
Selección de imagen base
La imagen base determina el punto de partida y afecta significativamente el tamaño final y la seguridad. Las opciones más comunes incluyen:
- Imágenes oficiales:
node:24
,python:3.14
,nginx:1.29.0-alpine
- Imágenes Alpine: más pequeñas pero con limitaciones de compatibilidad
- Imágenes Ubuntu/Debian: mayor compatibilidad pero más grandes
# Para aplicaciones de producción - imagen mínima
FROM node:24-alpine
# Para desarrollo o compatibilidad completa
FROM node:24
# Para máxima seguridad y tamaño mínimo
FROM gcr.io/distroless/nodejs24
La estructura de un Dockerfile bien diseñado equilibra la eficiencia de construcción, la reutilización de caché y la mantenibilidad del código, estableciendo las bases sólidas para crear imágenes Docker profesionales.
FROM, RUN, COPY, CMD
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
Las instrucciones fundamentales de un Dockerfile permiten definir el entorno base, ejecutar comandos, transferir archivos y establecer el comportamiento de ejecución del contenedor. Estas cuatro instrucciones constituyen el núcleo de cualquier Dockerfile profesional.
FROM: Definiendo la imagen base
La instrucción FROM establece la imagen base sobre la cual se construirá la nueva imagen. Es obligatoria y debe ser la primera instrucción no comentada en cualquier Dockerfile.
# Sintaxis básica
FROM <imagen>:<tag>
# Ejemplos comunes
FROM nginx:alpine
FROM python:3.11-slim
FROM mysql:8.0
FROM node:18
Mejores prácticas para FROM:
- Especifica siempre un tag explícito para garantizar reproducibilidad:
# ❌ Evitar - puede cambiar sin aviso
FROM python
# ✅ Recomendado - versión específica
FROM python:3.11-slim
- Utiliza imágenes oficiales verificadas cuando sea posible:
# Imágenes oficiales populares
FROM postgres:15-alpine
FROM nginx:1.25-alpine
FROM redis:7-alpine
- Considera el tamaño eligiendo variantes apropiadas:
# Para aplicaciones de producción ligeras
FROM python:3.11-alpine
# Para desarrollo con herramientas completas
FROM python:3.11
# Para máxima seguridad
FROM python:3.11-slim
RUN: Ejecutando comandos durante la construcción
La instrucción RUN ejecuta comandos en una nueva capa sobre la imagen actual y confirma los resultados. Se utiliza para instalar paquetes, crear directorios, configurar el sistema y realizar cualquier tarea necesaria durante la construcción.
Formas de uso de RUN:
- Shell form (más común):
RUN apt-get update && apt-get install -y python3
- Exec form (recomendada para mayor control):
RUN ["apt-get", "update"]
RUN ["/bin/bash", "-c", "echo hello"]
Optimización de comandos RUN:
# ❌ Múltiples capas innecesarias
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
# ✅ Una sola capa optimizada
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Ejemplos prácticos por tecnología:
- Para aplicaciones Python:
FROM python:3.11-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir --upgrade pip
- Para aplicaciones Node.js:
FROM node:18-alpine
RUN apk add --no-cache \
git \
python3 \
make \
g++
COPY: Transfiriendo archivos al contenedor
La instrucción COPY copia archivos o directorios desde el contexto de construcción hacia el sistema de archivos del contenedor. Es la forma preferida para agregar archivos locales a la imagen.
Sintaxis básica:
COPY <origen> <destino>
COPY <origen>... <destino>
COPY ["<origen>",... "<destino>"]
Ejemplos de uso común:
# Copiar un archivo específico
COPY requirements.txt /app/
# Copiar múltiples archivos
COPY package.json package-lock.json ./
# Copiar todo el directorio actual
COPY . /usr/src/app/
# Copiar con nuevo nombre
COPY config/nginx.conf /etc/nginx/sites-available/default
Buenas prácticas para COPY:
- Copia archivos de configuración primero para aprovechar el caché:
FROM node:18-alpine
WORKDIR /app
# Copiar solo archivos de dependencias primero
COPY package*.json ./
RUN npm ci --only=production
# Copiar código fuente al final
COPY src/ ./src/
COPY public/ ./public/
- Utiliza patrones específicos para mayor control:
# Copiar solo archivos Python
COPY *.py /app/
# Copiar archivos de configuración
COPY config/*.conf /etc/myapp/
# Excluir archivos temporales (usar .dockerignore)
COPY . /app/
- Establece permisos apropiados cuando sea necesario:
FROM nginx:alpine
# Copiar archivos de configuración con permisos específicos
COPY --chmod=644 nginx.conf /etc/nginx/nginx.conf
COPY --chown=nginx:nginx web/ /usr/share/nginx/html/
CMD: Definiendo el comando por defecto
La instrucción CMD especifica el comando que se ejecutará por defecto cuando se inicie un contenedor desde la imagen. Solo debe haber una instrucción CMD en un Dockerfile; si hay múltiples, solo la última tendrá efecto.
Tres formas de sintaxis:
- Exec form (preferida):
CMD ["executable", "param1", "param2"]
- Como parámetros por defecto para ENTRYPOINT:
CMD ["param1", "param2"]
- Shell form:
CMD command param1 param2
Ejemplos prácticos por tipo de aplicación:
- Aplicación web Python:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Comando por defecto para ejecutar la aplicación
CMD ["python", "app.py"]
- Servidor web Nginx:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY html/ /usr/share/nginx/html/
# Nginx se ejecuta en primer plano
CMD ["nginx", "-g", "daemon off;"]
- Base de datos PostgreSQL personalizada:
FROM postgres:15-alpine
COPY init-scripts/ /docker-entrypoint-initdb.d/
# Utilizar el CMD por defecto de la imagen base postgres
CMD ["postgres"]
Diferencias clave entre CMD y RUN:
- RUN se ejecuta durante la construcción de la imagen
- CMD se ejecuta cuando se inicia el contenedor
FROM ubuntu:22.04
# Se ejecuta durante docker build
RUN apt-get update && apt-get install -y python3
# Se ejecuta durante docker run
CMD ["python3", "--version"]
Combinación efectiva de instrucciones:
FROM node:18-alpine
# Crear directorio de trabajo
WORKDIR /usr/src/app
# Instalar dependencias globales
RUN npm install -g pm2
# Copiar archivos de dependencias
COPY package*.json ./
# Instalar dependencias del proyecto
RUN npm ci --only=production
# Copiar código fuente
COPY . .
# Crear usuario no-root
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001 && \
chown -R nextjs:nodejs /usr/src/app
USER nextjs
# Comando por defecto
CMD ["pm2-runtime", "start", "ecosystem.config.js"]
Estas cuatro instrucciones forman la columna vertebral de cualquier Dockerfile profesional, permitiendo crear imágenes robustas y eficientes que siguen las mejores prácticas de la industria.
ENTRYPOINT, ENV, EXPOSE
Estas instrucciones avanzadas de Dockerfile proporcionan un control más granular sobre el comportamiento del contenedor, la configuración del entorno y la exposición de servicios. Complementan las instrucciones básicas para crear imágenes más robustas y configurables.
ENTRYPOINT: Definiendo el punto de entrada inmutable
La instrucción ENTRYPOINT establece el comando que siempre se ejecutará cuando se inicie el contenedor, sin importar los argumentos pasados a docker run
. A diferencia de CMD, ENTRYPOINT no puede ser sobrescrito desde la línea de comandos.
Sintaxis y formas de uso:
- Exec form (recomendada):
ENTRYPOINT ["executable", "param1", "param2"]
- Shell form:
ENTRYPOINT command param1 param2
Diferencias clave entre ENTRYPOINT y CMD:
# Solo con CMD - puede sobrescribirse completamente
FROM alpine
CMD ["echo", "Hello World"]
# Con ENTRYPOINT - comando fijo
FROM alpine
ENTRYPOINT ["echo", "Hello"]
CMD ["World"]
Comportamiento en ejecución:
# Con CMD solamente
docker run myimage ls /tmp # Ejecuta: ls /tmp (sobrescribe echo)
# Con ENTRYPOINT
docker run myimage ls /tmp # Ejecuta: echo ls /tmp
Casos de uso prácticos:
- Aplicación con parámetros configurables:
FROM python:3.11-slim
WORKDIR /app
COPY . .
# El script siempre se ejecuta, pero acepta argumentos
ENTRYPOINT ["python", "manage.py"]
CMD ["runserver", "0.0.0.0:8000"]
# Uso por defecto
docker run myapp # Ejecuta: python manage.py runserver 0.0.0.0:8000
# Con argumentos personalizados
docker run myapp migrate # Ejecuta: python manage.py migrate
- Herramienta CLI containerizada:
FROM alpine
RUN apk add --no-cache curl jq
COPY cli-tool.sh /usr/local/bin/cli-tool
RUN chmod +x /usr/local/bin/cli-tool
# La herramienta siempre se ejecuta
ENTRYPOINT ["/usr/local/bin/cli-tool"]
- Inicialización y configuración:
FROM nginx:alpine
COPY entrypoint.sh /docker-entrypoint.sh
COPY nginx.conf.template /etc/nginx/nginx.conf.template
RUN chmod +x /docker-entrypoint.sh
# Script de inicialización que prepara el entorno
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
ENV: Configurando variables de entorno
La instrucción ENV establece variables de entorno que estarán disponibles tanto durante la construcción de la imagen como en tiempo de ejecución del contenedor. Es fundamental para crear aplicaciones configurables y seguir principios de twelve-factor app.
Sintaxis básica:
ENV <clave>=<valor>
ENV <clave1>=<valor1> <clave2>=<valor2>
Ejemplos de configuración por tecnología:
- Aplicación Node.js:
FROM node:18-alpine
# Variables de entorno para Node.js
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
# Configuración de npm
ENV NPM_CONFIG_LOGLEVEL=warn
ENV NPM_CONFIG_PROGRESS=false
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE $PORT
CMD ["npm", "start"]
- Aplicación Python con Django:
FROM python:3.11-slim
# Variables de configuración de Django
ENV DJANGO_SETTINGS_MODULE=myproject.settings.production
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Configuración de la aplicación
ENV DEBUG=False
ENV DATABASE_URL=postgresql://user:pass@localhost/dbname
ENV ALLOWED_HOSTS=localhost,127.0.0.1
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]
Variables de entorno en tiempo de construcción:
FROM postgres:15-alpine
# Configuración de PostgreSQL
ENV POSTGRES_DB=myapp
ENV POSTGRES_USER=appuser
ENV POSTGRES_PASSWORD=secure_password
# Configuración de localización
ENV LANG=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
# Las variables ENV están disponibles en RUN
RUN echo "Database: $POSTGRES_DB" > /tmp/db-info.txt
Reutilización de variables:
FROM nginx:alpine
# Definir variables base
ENV APP_VERSION=1.2.0
ENV APP_NAME=mywebapp
# Usar variables en otras configuraciones
ENV LOG_PATH=/var/log/$APP_NAME
ENV CONFIG_PATH=/etc/$APP_NAME
# Crear directorios usando las variables
RUN mkdir -p $LOG_PATH $CONFIG_PATH
LABEL version=$APP_VERSION \
name=$APP_NAME
Buenas prácticas con ENV:
- Agrupa variables relacionadas para mejor legibilidad:
# Variables de configuración de la aplicación
ENV APP_NAME=myservice \
APP_VERSION=2.1.0 \
APP_ENV=production
# Variables de base de datos
ENV DB_HOST=localhost \
DB_PORT=5432 \
DB_NAME=myservice_db
EXPOSE: Documentando puertos de red
La instrucción EXPOSE documenta los puertos que la aplicación utiliza dentro del contenedor. Es importante entender que EXPOSE no publica automáticamente los puertos al host; solo sirve como documentación y metadatos.
Sintaxis y uso básico:
EXPOSE <puerto>
EXPOSE <puerto>/<protocolo>
Ejemplos por tipo de aplicación:
- Aplicación web estándar:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# Documentar que la aplicación usa el puerto 3000
EXPOSE 3000
CMD ["npm", "start"]
- Aplicación con múltiples servicios:
FROM nginx:alpine
# Servidor web HTTP
EXPOSE 80
# Servidor web HTTPS
EXPOSE 443
COPY nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]
- Base de datos con protocolo específico:
FROM postgres:15-alpine
# PostgreSQL utiliza TCP en puerto 5432
EXPOSE 5432/tcp
ENV POSTGRES_DB=myapp
ENV POSTGRES_USER=user
ENV POSTGRES_PASSWORD=password
Uso con variables de entorno:
FROM python:3.11-slim
# Variable configurable para el puerto
ENV PORT=8000
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Usar la variable ENV en EXPOSE
EXPOSE $PORT
CMD ["python", "-m", "gunicorn", "--bind", "0.0.0.0:$PORT", "app:application"]
Múltiples puertos y protocolos:
FROM alpine
# Aplicación que maneja múltiples protocolos
EXPOSE 8080/tcp
EXPOSE 8443/tcp
EXPOSE 53/udp
EXPOSE 161/udp
# También se pueden especificar múltiples puertos en una línea
EXPOSE 9090 9091 9092
Integración efectiva de las tres instrucciones
Ejemplo completo de aplicación web:
FROM node:18-alpine
# Variables de configuración
ENV NODE_ENV=production \
PORT=3000 \
HOST=0.0.0.0 \
LOG_LEVEL=info
# Crear usuario no-root
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
WORKDIR /app
# Instalar dependencias
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Copiar código fuente
COPY --chown=nextjs:nodejs . .
USER nextjs
# Documentar puerto utilizado
EXPOSE $PORT
# Script de inicialización como entrypoint
ENTRYPOINT ["./docker-entrypoint.sh"]
# Comando por defecto
CMD ["npm", "start"]
Ejemplo de herramienta CLI configurable:
FROM python:3.11-alpine
# Configuración de la herramienta
ENV TOOL_CONFIG_PATH=/etc/mytool \
TOOL_DATA_PATH=/var/lib/mytool \
TOOL_LOG_LEVEL=INFO
# Instalar herramienta
RUN pip install mytool
# Crear directorios de configuración
RUN mkdir -p $TOOL_CONFIG_PATH $TOOL_DATA_PATH
# Copiar configuración por defecto
COPY config/ $TOOL_CONFIG_PATH/
# Punto de entrada fijo para la herramienta
ENTRYPOINT ["mytool"]
# Comando por defecto
CMD ["--help"]
La combinación efectiva de ENTRYPOINT, ENV y EXPOSE permite crear imágenes Docker profesionales que son configurables, documentadas y mantienen un comportamiento predecible, estableciendo las bases para aplicaciones robustas en entornos containerizados.
Aprendizajes de esta lección de Docker
- Comprender la estructura y anatomía básica de un Dockerfile.
- Aprender a usar las instrucciones fundamentales: FROM, RUN, COPY y CMD.
- Conocer instrucciones avanzadas como ENTRYPOINT, ENV y EXPOSE para configurar contenedores.
- Aplicar buenas prácticas para optimizar la construcción y reutilización de capas.
- Entender la importancia del orden y la selección de la imagen base para eficiencia y seguridad.
Completa este curso de Docker y certifícate
Únete a nuestra plataforma de cursos de programación y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs