Imagina poder ejecutar una aplicación exactamente igual en tu computadora, en la de tu equipo y en un servidor en producción, sin errores inesperados, sin conflictos de versiones y sin perder horas configurando el entorno.
Eso que durante años fue un dolor de cabeza para desarrolladores y empresas… Docker lo resolvió.
Docker es hoy una de las tecnologías más importantes del desarrollo moderno. Ha transformado la manera en que se construyen, prueban y despliegan aplicaciones, permitiendo trabajar con entornos reproducibles, ligeros y altamente portables. Por eso es una herramienta clave tanto para desarrolladores web, ingenieros de software, DevOps y empresas que buscan velocidad, estabilidad y escalabilidad.
En esta guía completa aprenderás Docker desde cero y con profundidad, con explicaciones claras, ejemplos prácticos y buenas prácticas reales. No importa si estás dando tus primeros pasos o si ya usas Docker y quieres dominarlo como un profesional: aquí encontrarás el conocimiento bien organizado y explicado de forma sencilla.
A lo largo del artículo descubrirás:
- Qué es Docker y cómo funciona realmente
- Los conceptos fundamentales que necesitas entender sin memorizar de más
- Cómo instalarlo y usarlo paso a paso
- Cómo crear tus propias imágenes y contenedores
- Cómo trabajar con volúmenes, redes y Docker Compose
- Mejores prácticas usadas en entornos profesionales
- Casos reales de uso en desarrollo web y producción
Si alguna vez escuchaste frases como “en mi máquina sí funciona”, “el servidor está mal configurado” o “el entorno nos rompió la app”, Docker es la respuesta que estabas buscando.
Vamos a entenderlo, dominarlo y usarlo correctamente.
Empecemos!
Ver índice del contenido
1. ¿Qué es Docker?
Docker es una tecnología que permite crear, empaquetar y ejecutar aplicaciones dentro de contenedores, garantizando que se comporten exactamente igual en cualquier entorno: desarrollo, pruebas o producción.
Su mayor aporte es resolver uno de los problemas más antiguos y frustrantes del desarrollo de software:
“Funciona en mi computadora, pero no en el servidor.”
Con Docker, este problema desaparece. La razón es simple: Docker encapsula la aplicación junto con todas sus dependencias, librerías, configuraciones y versiones necesarias, creando un entorno completamente controlado y reproducible.
De esta forma, la aplicación no depende del sistema donde se ejecuta, sino de su propio contenedor. El resultado es un software más predecible, portable y confiable, que puede moverse sin cambios entre distintos equipos, servidores o servicios en la nube.
1.1 Definición de Docker
Docker es una plataforma de contenedores que permite ejecutar aplicaciones de forma ligera, portátil y consistente.
En términos simples:
- Docker empaqueta una aplicación
- Incluye sus dependencias, librerías y configuración
- La ejecuta en un entorno aislado llamado contenedor
Ese contenedor puede moverse y ejecutarse sin cambios en:
- Tu laptop
- La computadora de otro desarrollador
- Un servidor en la nube
- Un entorno de producción
Docker no es un lenguaje de programación ni un sistema operativo.
Es una herramienta de virtualización a nivel de aplicación, mucho más eficiente que las soluciones tradicionales.
1.2 ¿Cómo funciona Docker internamente?
Para entender cómo funciona Docker internamente, primero hay que entender cómo funciona un sistema operativo y qué problema intenta resolver Docker.
Tradicionalmente, para aislar aplicaciones se usaban máquinas virtuales, cada una con su propio sistema operativo completo. Esto garantizaba aislamiento, pero a un costo alto en recursos y rendimiento. Docker adopta un enfoque diferente y mucho más eficiente.

Aislamiento sin un sistema operativo completo
Docker no crea sistemas operativos virtuales.
En su lugar, aprovecha características nativas del sistema operativo (principalmente de Linux) para aislar aplicaciones a nivel de procesos.
Docker utiliza mecanismos del kernel como:
- Namespaces → aíslan procesos, red, usuarios y sistema de archivos
- Cgroups (control groups) → limitan el uso de CPU, memoria y otros recursos
- Sistema de archivos por capas → permite compartir componentes sin duplicarlos
Gracias a esto, cada contenedor se comporta como si fuera un sistema independiente, aunque en realidad comparte el kernel del sistema operativo anfitrión.
¿Qué significa esto en la práctica?
Cada contenedor Docker:
- Se ejecuta como un proceso independiente en el sistema
- Comparte el kernel del sistema operativo
- Tiene su propio sistema de archivos
- Tiene su propia red y configuración
- Está aislado del resto de contenedores
Este enfoque hace que los contenedores:
- Arranquen en segundos
- Consuman muy pocos recursos
- Sean mucho más eficientes que una máquina virtual
Por eso Docker es ligero, rápido y altamente escalable.
Componentes internos clave de Docker
Para que todo esto funcione, Docker se apoya en varios componentes fundamentales.
Docker Engine
Es el corazón de Docker.
Docker Engine es el servicio que se ejecuta en segundo plano y se encarga de:
- Construir imágenes a partir de Dockerfiles
- Crear y ejecutar contenedores
- Gestionar redes y volúmenes
- Comunicarse con el sistema operativo
Cuando ejecutas un comando como docker run, en realidad estás enviando instrucciones al Docker Engine.
Imágenes Docker
Una imagen Docker es una plantilla inmutable que contiene todo lo necesario para ejecutar una aplicación:
- Un sistema base mínimo
- La aplicación
- Librerías y dependencias
- Configuración inicial
Las imágenes se construyen en capas, lo que permite:
- Reutilizar partes comunes
- Reducir tamaño
- Acelerar la creación de contenedores
Una imagen no se ejecuta por sí sola; necesita convertirse en un contenedor.
Contenedores
Un contenedor es una imagen en ejecución.
Cuando Docker crea un contenedor:
- Usa una imagen como base
- Agrega una capa de escritura
- Ejecuta el proceso principal definido
Una misma imagen puede generar:
- Un contenedor
- Diez contenedores
- Cientos de contenedores
Todos idénticos y aislados entre sí.
Dockerfile
El Dockerfile es un archivo de texto que describe paso a paso cómo construir una imagen.
En él se define:
- Qué imagen base usar
- Qué dependencias instalar
- Qué archivos copiar
- Qué comando ejecutar al iniciar el contenedor
Gracias al Dockerfile, la construcción de imágenes es:
- Automática
- Reproducible
- Versionable
Flujo simplificado de funcionamiento de Docker
El funcionamiento general de Docker puede resumirse así:
- El desarrollador escribe un Dockerfile
- Docker construye una imagen a partir de ese archivo
- La imagen se almacena localmente o en un registro
- La imagen se ejecuta como uno o varios contenedores
- Los contenedores corren de forma aislada y controlada
Todo este proceso es rápido, predecible y repetible.
📌 Idea clave
Docker funciona porque no virtualiza sistemas operativos, sino que aísla aplicaciones usando el propio sistema operativo, logrando una eficiencia que antes no era posible.
1.3 Docker vs Máquinas Virtuales
Esta comparación es clave para entender por qué Docker revolucionó el desarrollo.
Máquinas Virtuales (VM)
- Incluyen un sistema operativo completo
- Son pesadas (varios GB)
- Consumen muchos recursos
- Arrancan lento
- Cada VM es un sistema independiente
Docker (Contenedores)
- Comparten el sistema operativo anfitrión
- Son livianos (MB)
- Consumen pocos recursos
- Arrancan en segundos
- Ejecutan solo lo necesario
Comparación clara
| Característica | Docker | Máquina Virtual |
|---|---|---|
| Tamaño | Muy ligero | Muy pesado |
| Arranque | Segundos | Minutos |
| Consumo de recursos | Bajo | Alto |
| Portabilidad | Muy alta | Media |
| Ideal para | Aplicaciones | Sistemas completos |
Docker no reemplaza a las máquinas virtuales, pero las complementa y, en muchos casos, las supera para desarrollo y despliegue de aplicaciones.
1.4 Ventajas de usar Docker
Docker no se ha convertido en un estándar por casualidad. Su adopción masiva se debe a una serie de ventajas prácticas que resuelven problemas reales en el desarrollo y despliegue de aplicaciones.
a. Portabilidad total:
Una aplicación Dockerizada se ejecuta exactamente igual en cualquier entorno, ya sea en desarrollo, pruebas o producción. Esto elimina las diferencias entre entornos y reduce significativamente los errores que aparecen al mover una aplicación de un sistema a otro.
b. Rapidez y eficiencia:
Docker utiliza contenedores ligeros que arrancan en cuestión de segundos. Al compartir el sistema operativo anfitrión, los contenedores consumen muchos menos recursos que las soluciones tradicionales, permitiendo un uso más eficiente de la CPU y la memoria.
c. Aislamiento de aplicaciones:
Cada aplicación se ejecuta en su propio entorno aislado. Esto significa que sus dependencias, configuraciones y procesos no interfieren con los de otras aplicaciones, haciendo posible ejecutar múltiples servicios en un mismo servidor sin conflictos.
d. Trabajo en equipo simplificado:
Docker facilita el trabajo colaborativo al asegurar que todos los desarrolladores utilicen el mismo entorno. De esta manera se evitan errores de configuración local y conflictos de versiones, y el entorno de ejecución queda claramente definido por el proyecto.
e. Escalabilidad sencilla:
Docker permite escalar aplicaciones de forma simple y controlada. Los contenedores pueden replicarse fácilmente para manejar mayor carga de trabajo, y su integración con herramientas de orquestación como Kubernetes permite administrar aplicaciones distribuidas de manera eficiente.
f. Ideal para DevOps y CI/CD:
Docker encaja de forma natural en flujos de trabajo DevOps. Permite automatizar procesos de integración continua y despliegue continuo, garantizando que el software se pruebe y se despliegue siempre en el mismo entorno, reduciendo errores y acelerando las entregas.
g. Uso profesional y empresarial:
Docker es utilizado por startups, empresas medianas y grandes corporaciones. Su adopción a nivel empresarial lo ha convertido en un estándar de la industria, con un ecosistema maduro, una comunidad amplia y herramientas de soporte profesional.
📌 En resumen, Docker permite desarrollar más rápido, con menos errores y con mayor control sobre los entornos.
Por eso se ha convertido en una herramienta imprescindible del desarrollo moderno.
2. Conceptos Básicos de Docker
Para entender Docker de verdad —y no solo usarlo «porque todos lo usan»— es clave dominar sus conceptos fundamentales. Docker introduce una nueva forma de empaquetar, distribuir y ejecutar aplicaciones, y todo gira alrededor de unas pocas ideas muy bien definidas.
En esta sección vamos a desarmar Docker pieza por pieza, con explicaciones claras y ejemplos mentales simples.
2.1 ¿Qué es un Contenedor (Container)?
Un contenedor es una instancia en ejecución de una aplicación, empaquetada junto con todo lo que necesita para funcionar correctamente.
En términos simples: un contenedor es una aplicación aislada, portable y reproducible.
Dentro de un contenedor viven:
- La aplicación (por ejemplo, un servidor web)
- Sus dependencias
- Sus librerías
- Su configuración mínima
- Su propio sistema de archivos
Lo que NO incluye un contenedor: El kernel del sistema operativo. Los contenedores comparten el kernel del host, lo que los hace mucho más ligeros y rápidos que las máquinas virtuales tradicionales.
Todo esto se ejecuta de forma aislada del sistema operativo anfitrión, pero sin necesidad de crear una máquina virtual completa, lo que lo hace increíblemente eficiente.
Una buena forma de imaginarlo es esta: Si el sistema operativo es un edificio, cada contenedor es un departamento independiente. Todos comparten la estructura del edificio (el kernel), pero cada uno tiene su propio espacio, reglas y recursos controlados.
Nota importante: En Windows y macOS, Docker Desktop incluye una máquina virtual ligera que proporciona el kernel Linux necesario, ya que Docker fue diseñado originalmente para Linux. En Linux nativo, los contenedores corren directamente sobre el kernel del sistema.
2.2 ¿Qué es una Imagen (Image)?
Una imagen Docker es una plantilla inmutable que contiene todo lo necesario para crear uno o varios contenedores.
La imagen no se ejecuta por sí sola. Es como un molde o una receta. Cuando Docker «corre» una imagen, lo que hace es crear un contenedor basado en ella.
Una imagen define:
- Qué sistema base se usa (por ejemplo, Linux Alpine, Ubuntu, Debian)
- Qué software se instala
- Qué archivos se copian
- Qué variables de entorno se configuran
- Qué comando se ejecuta al iniciar el contenedor
Gracias a esto, cualquier persona puede usar exactamente la misma imagen y obtener el mismo comportamiento, sin importar si está en Windows, Linux o macOS.
2.3 Diferencias entre Imagen y Contenedor
Aunque están muy relacionados, imagen y contenedor no son lo mismo, y confundirlos es uno de los errores más comunes al empezar con Docker.
La imagen es algo estático: no cambia, no se ejecuta y sirve como base.
El contenedor es algo dinámico: es la imagen en ejecución, con estado, procesos activos y cambios temporales.
Dicho de forma directa:
- Una imagen es el plano
- Un contenedor es la casa construida y habitada
Desde una sola imagen puedes crear muchos contenedores, todos iguales al inicio, pero con comportamientos independientes mientras están en ejecución.
Sobre el estado de los contenedores: Los cambios que hagas dentro de un contenedor en ejecución son efímeros por defecto. Esto significa que si eliminas el contenedor, esos cambios se pierden. Para mantener datos de forma permanente, necesitas usar volúmenes, que veremos más adelante.
2.4 Capas (Layers) de una Imagen Docker
Las imágenes Docker están construidas por capas, llamadas layers.
Cada capa representa un cambio concreto:
- Instalar un paquete
- Copiar un archivo
- Definir una variable de entorno
- Ejecutar un comando
Estas capas se apilan unas sobre otras y Docker las reutiliza inteligentemente. Si dos imágenes comparten capas, Docker no las duplica, lo que ahorra espacio y acelera descargas.
Característica importante de las capas: Las capas de una imagen son de solo lectura. Cuando creas un contenedor, Docker añade una capa superior de lectura-escritura donde se guardan los cambios temporales del contenedor en ejecución.
Esta arquitectura por capas tiene dos grandes ventajas:
- Eficiencia: menos uso de disco y red
- Velocidad: reconstrucciones más rápidas al modificar una imagen
Por eso Docker es tan rápido comparado con soluciones tradicionales de virtualización.
2.5 ¿Qué es un Dockerfile?
Un Dockerfile es un archivo de texto que contiene las instrucciones para construir una imagen Docker.
En él se describe paso a paso:
- Qué imagen base usar
- Qué dependencias instalar
- Qué archivos copiar
- Qué puertos exponer
- Qué comando ejecutar al iniciar
Es, básicamente, la receta oficial de la imagen.
Gracias al Dockerfile, el proceso de creación de imágenes es:
- Automático: se ejecuta con un solo comando
- Repetible: produce el mismo resultado cada vez
- Versionable: se puede guardar en Git junto al código
Esto convierte a Docker en una herramienta perfecta para entornos profesionales, CI/CD y trabajo en equipo.
2.6 ¿Qué es un Registro (Registry)?
Un registro Docker es un repositorio donde se almacenan imágenes Docker para poder compartirlas, descargarlas o publicarlas.
Funciona de forma similar a GitHub, pero en lugar de código, guarda imágenes.
Desde un registro puedes:
- Subir imágenes propias
- Descargar imágenes de otros desarrolladores
- Versionar imágenes con etiquetas (tags)
- Controlar accesos y permisos
Docker, por defecto, se conecta a Docker Hub (docker.io), el registro público oficial, pero también permite configurar y usar registros privados para mayor seguridad y control.
2.7 Docker Hub y Registros Privados
Docker Hub es el registro público oficial de Docker. Allí encontrarás miles de imágenes listas para usar: bases de datos, servidores web, lenguajes de programación y mucho más.
Docker Hub es ideal para:
- Aprender y experimentar
- Prototipar rápidamente
- Proyectos pequeños o públicos
- Usar imágenes oficiales verificadas
Sin embargo, en entornos empresariales o proyectos sensibles, lo común es usar registros privados, que permiten:
- Control total sobre las imágenes
- Mayor seguridad y cumplimiento normativo
- Integración con sistemas internos de CI/CD
- Gestión de accesos y permisos granulares
Algunos registros privados populares incluyen:
- Amazon ECR (Elastic Container Registry)
- Google Container Registry (GCR) / Artifact Registry
- Azure Container Registry (ACR)
- GitHub Container Registry
- GitLab Container Registry
- Harbor (solución open-source auto-hospedada)
Estos registros pueden estar en la nube o en servidores propios, manteniendo el mismo flujo de trabajo que Docker Hub.
3. Instalación de Docker
Instalar Docker es el primer paso práctico para empezar a trabajar con contenedores. Aunque Docker funciona en múltiples sistemas operativos, la forma de instalarlo y cómo se ejecuta internamente varía según la plataforma.
En esta sección veremos qué necesitas antes de instalar Docker, cómo hacerlo en Windows, macOS y Linux, cómo verificar que todo funcione correctamente y cuáles son los problemas más comunes que suelen aparecer durante la instalación.
3.1 Requisitos previos
Antes de instalar Docker, es importante entender que Docker depende fuertemente del sistema operativo y de la virtualización.
En términos generales, necesitas:
- Un sistema operativo moderno y actualizado
- Arquitectura de 64 bits
- Soporte de virtualización habilitado en el equipo (para Windows y macOS)
- Al menos 4 GB de RAM disponible para asignar a Docker (8 GB recomendados para desarrollo)
Sobre la virtualización: En Windows y macOS, Docker se ejecuta mediante Docker Desktop, que incluye una máquina virtual ligera con Linux. Por eso, es indispensable que la virtualización esté activada en el BIOS o UEFI del equipo (Intel VT-x o AMD-V).
En Linux, la situación es diferente: Docker corre directamente sobre el kernel del sistema, por lo que no necesita una máquina virtual adicional. Esto lo hace más eficiente y simple a nivel técnico.
Nota importante sobre licenciamiento: Docker Desktop es gratuito para uso personal, educación y pequeñas empresas. Las organizaciones con más de 250 empleados o ingresos anuales superiores a $10 millones USD requieren una licencia de pago. Docker Engine en Linux es completamente gratuito y open-source.
3.2 Instalación de Docker en Windows
En Windows, la forma oficial y recomendada de instalar Docker es mediante Docker Desktop.
Docker Desktop incluye:
- Docker Engine (el motor de contenedores)
- Docker CLI (interfaz de línea de comandos)
- Docker Compose
- Una máquina virtual Linux interna
- Interfaz gráfica para gestionar contenedores e imágenes
Requisitos específicos para Windows:
Para que Docker funcione correctamente en Windows, debes cumplir estas condiciones:
- Windows 10 de 64 bits: Pro, Enterprise o Education (build 19041 o superior)
- Windows 11 de 64 bits: Cualquier versión
- WSL 2 habilitado (recomendado y backend por defecto)
- Alternativa: Hyper-V y Contenedores de Windows (backend legacy)
Actualmente, Docker Desktop utiliza WSL 2 como backend principal, ya que ofrece mejor rendimiento, compatibilidad y menor consumo de recursos que Hyper-V.
Proceso de instalación:
- Descarga el instalador desde el sitio oficial de Docker (https://www.docker.com/products/docker-desktop)
- Ejecuta el instalador
Docker Desktop Installer.exe - Sigue los pasos del asistente (se recomienda dejar las opciones por defecto)
- Durante la instalación, Docker configura automáticamente WSL 2 si no está presente
- Reinicia el sistema cuando se te solicite
- Inicia Docker Desktop desde el menú de Windows
Una vez instalado, Docker Desktop se ejecuta como una aplicación gráfica desde donde puedes controlar contenedores, imágenes, volúmenes, redes, recursos asignados y configuraciones.
Verificar WSL 2:
Para confirmar que WSL 2 está correctamente instalado y configurado, abre PowerShell y ejecuta:
wsl --list --verbose
Deberías ver una distribución de Linux con VERSION 2.
3.3 Instalación de Docker en macOS
En macOS, Docker también se instala mediante Docker Desktop, de forma muy similar a Windows.
Docker Desktop para macOS funciona creando una máquina virtual Linux ligera, ya que macOS no comparte el kernel Linux necesario para ejecutar contenedores directamente.
Compatibilidad:
Docker es compatible con:
- Macs con procesadores Intel (x86_64)
- Macs con Apple Silicon (M1, M2, M3, M4 – arquitectura ARM64)
El instalador detecta automáticamente la arquitectura y descarga la versión adecuada.
Requisitos:
- macOS 11 (Big Sur) o superior
- Al menos 4 GB de RAM (8 GB recomendados)
Proceso de instalación:
- Descarga el archivo
.dmgcorrecto para tu arquitectura desde https://www.docker.com/products/docker-desktop- Docker Desktop for Mac with Intel chip
- Docker Desktop for Mac with Apple silicon
- Abre el archivo
.dmgdescargado - Arrastra el ícono de Docker a la carpeta Aplicaciones
- Abre Docker desde Aplicaciones
- Concede los permisos necesarios cuando se te solicite
- Espera a que Docker inicie completamente
Una vez iniciado, Docker Desktop se ejecuta en segundo plano y queda accesible desde la barra de menú (ícono de ballena).
Desde allí puedes gestionar recursos como CPU, memoria y disco asignados a Docker, algo especialmente útil en equipos portátiles para optimizar el rendimiento y la duración de la batería.
3.4 Instalación de Docker en Linux
En Linux, Docker se instala de forma nativa y directa sobre el sistema operativo, sin necesidad de Docker Desktop ni máquinas virtuales adicionales (aunque Docker Desktop también está disponible como opción con interfaz gráfica desde 2022).
Opciones de instalación en Linux:
Opción 1: Docker Engine (CLI, recomendado para servidores)
- Instalación más ligera
- Sin interfaz gráfica
- Ideal para servidores y producción
- Completamente gratuito y open-source
Opción 2: Docker Desktop para Linux
- Incluye interfaz gráfica
- Similar a la experiencia en Windows/macOS
- Disponible para Ubuntu, Debian y Fedora
- Sujeto a las mismas restricciones de licencia
Distribuciones compatibles con Docker Engine:
- Ubuntu (20.04 LTS, 22.04 LTS, 24.04 LTS)
- Debian (10, 11, 12)
- Fedora (38, 39, 40)
- CentOS / RHEL (7, 8, 9)
- Arch Linux
- openSUSE
Proceso de instalación (Docker Engine en Ubuntu/Debian):
La instalación se realiza mediante el gestor de paquetes de cada distribución. Para Ubuntu/Debian:
# 1. Actualizar el índice de paquetes sudo apt-get update # 2. Instalar dependencias necesarias sudo apt-get install ca-certificates curl gnupg # 3. Añadir la clave GPG oficial de Docker sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # 4. Configurar el repositorio echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 5. Actualizar el índice de paquetes nuevamente sudo apt-get update # 6. Instalar Docker Engine sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Configuración post-instalación en Linux:
Una vez instalado, Docker se ejecuta como un servicio del sistema. Es importante realizar estos pasos adicionales:
1. Iniciar y habilitar el servicio Docker:
# Iniciar Docker sudo systemctl start docker # Habilitar Docker para que inicie automáticamente al arrancar el sistema sudo systemctl enable docker # Verificar el estado sudo systemctl status docker
2. Configurar permisos de usuario (opcional pero recomendado):
Por defecto, Docker requiere privilegios de administrador. Para usar Docker sin sudo:
# Agregar tu usuario al grupo docker sudo usermod -aG docker $USER # Aplicar los cambios (o cerrar sesión y volver a iniciar) newgrp docker
Nota de seguridad: Agregar un usuario al grupo docker otorga privilegios equivalentes a root. Solo hazlo en entornos de desarrollo o con usuarios de confianza.
Esta instalación es la más eficiente y la preferida en servidores y entornos de producción.
3.5 Verificar la instalación
Después de instalar Docker, es fundamental comprobar que todo funcione correctamente.
Verificar la versión instalada:
docker --version
Esto mostrará algo como: Docker version 24.0.7, build afdd53b
Para información más detallada:
docker version
Esto muestra información tanto del cliente como del servidor Docker.
Ejecutar el contenedor de prueba:
La forma más simple y universal de verificar que Docker funciona completamente es ejecutar el contenedor de prueba oficial:
docker run hello-world
Cuando ejecutas este comando, Docker:
- Busca la imagen
hello-worldlocalmente - Si no la encuentra, la descarga automáticamente desde Docker Hub
- Crea un contenedor basado en esa imagen
- Ejecuta el contenedor
- Muestra un mensaje confirmando que Docker funciona correctamente
- El contenedor se detiene automáticamente
Salida esperada:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
...
Si ves este mensaje, significa que Docker está instalado, configurado y funcionando como se espera.
Verificar que Docker Compose está disponible:
docker compose version
Esto confirmará que Docker Compose (incluido en Docker Desktop y en las instalaciones modernas de Docker Engine) está funcionando.
3.6 Problemas comunes de instalación
Aunque Docker suele instalarse sin mayores inconvenientes, existen algunos problemas frecuentes, especialmente en equipos personales.
Problema 1: Virtualización deshabilitada (Windows/macOS)
Síntoma: Docker Desktop no inicia o muestra un error sobre virtualización.
Causa: La virtualización (Intel VT-x o AMD-V) está deshabilitada en el BIOS/UEFI.
Solución:
- Reinicia el equipo y entra al BIOS/UEFI (generalmente F2, F10, DEL o ESC)
- Busca opciones como «Virtualization Technology», «Intel VT-x», «AMD-V» o «SVM Mode»
- Habilita la opción
- Guarda los cambios y reinicia
Problema 2: WSL 2 no configurado correctamente (Windows)
Síntoma: Error al iniciar Docker Desktop sobre WSL 2.
Causa: WSL 2 no está instalado, no está actualizado o no está configurado como versión predeterminada.
Solución:
# Instalar WSL 2 wsl --install # Actualizar WSL a la última versión wsl --update # Establecer WSL 2 como versión predeterminada wsl --set-default-version 2
Reinicia el equipo después de estos comandos.
Problema 3: Permisos insuficientes (Linux)
Síntoma: Error «permission denied» al ejecutar comandos Docker.
Causa: El usuario no pertenece al grupo docker.
Solución:
# Agregar usuario al grupo docker sudo usermod -aG docker $USER # Aplicar cambios newgrp docker # O cerrar sesión y volver a iniciar
Problema 4: Conflicto de puertos
Síntoma: Contenedores no pueden iniciar o muestran errores de «port already in use».
Causa: Servicios existentes (Apache, MySQL, PostgreSQL, etc.) están usando los puertos que el contenedor intenta utilizar.
Solución:
- Detén los servicios conflictivos
- O modifica el mapeo de puertos del contenedor (veremos esto más adelante)
Ejemplo: si MySQL local usa el puerto 3306, mapea el contenedor a otro puerto:
docker run -p 3307:3306 mysql
Problema 5: Versiones antiguas de Docker
Síntoma: Comandos o funcionalidades nuevas no funcionan.
Causa: Docker desactualizado o instalación previa corrupta.
Solución:
- Desinstala completamente la versión antigua
- Elimina archivos de configuración residuales
- Instala la versión más reciente desde el sitio oficial
Problema 6: Software de virtualización incompatible
Síntoma: Docker Desktop no inicia o entra en conflicto con otro software.
Causa: VirtualBox, VMware u otro software de virtualización puede entrar en conflicto con Hyper-V o WSL 2.
Solución:
- Usa WSL 2 como backend (generalmente compatible)
- O desactiva temporalmente el otro software de virtualización
Obtener ayuda adicional
La buena noticia es que Docker proporciona mensajes de error bastante claros, y la mayoría de estos problemas tienen solución rápida una vez identificada la causa.
Si encuentras un problema no listado aquí, consulta:
- La documentación oficial de Docker (https://docs.docker.com)
- Los foros de la comunidad Docker
- Stack Overflow con la etiqueta [docker]
- Los logs de Docker Desktop (disponibles en el menú de configuración)
4. Comandos Básicos de Docker
Una vez que Docker está instalado, el siguiente paso es aprender a interactuar con él. Docker se controla principalmente desde la línea de comandos (CLI), y aunque al inicio puede parecer intimidante, en realidad el flujo de trabajo se basa en unos pocos comandos fundamentales.
Dominar estos comandos te permitirá ejecutar contenedores, gestionar imágenes, depurar aplicaciones y controlar todo el ciclo de vida de tus servicios Docker.
Tabla de referencia rápida
Antes de profundizar en cada comando, aquí tienes una referencia rápida de los comandos más usados:
| Comando | Descripción |
|---|---|
docker --version | Muestra la versión de Docker |
docker images | Lista todas las imágenes locales |
docker pull <imagen> | Descarga una imagen desde Docker Hub |
docker run <imagen> | Crea y ejecuta un contenedor |
docker ps | Lista contenedores en ejecución |
docker ps -a | Lista todos los contenedores |
docker stop <contenedor> | Detiene un contenedor de forma controlada |
docker start <contenedor> | Inicia un contenedor detenido |
docker restart <contenedor> | Reinicia un contenedor |
docker rm <contenedor> | Elimina un contenedor |
docker rmi <imagen> | Elimina una imagen |
docker logs <contenedor> | Muestra los logs de un contenedor |
docker exec -it <contenedor> <comando> | Ejecuta un comando dentro de un contenedor |
docker build -t <nombre> . | Construye una imagen desde un Dockerfile |
docker inspect <contenedor/imagen> | Muestra información detallada |
4.1 Verificar la versión de Docker
El primer comando que todo usuario de Docker debería conocer sirve para confirmar que Docker está correctamente instalado y accesible desde la terminal.
docker --version
Salida esperada:
Docker version 24.0.7, build afdd53b
Este comando devuelve la versión instalada del cliente Docker. Si obtienes una respuesta con el número de versión, significa que Docker está disponible y funcionando a nivel básico.
Para una verificación más completa que muestre información tanto del cliente como del servidor (Docker Engine):
docker version
Salida esperada:
Client: Docker Engine - Community Version: 24.0.7 API version: 1.43 Go version: go1.20.10 ... Server: Docker Engine - Community Engine: Version: 24.0.7 API version: 1.43 (minimum version 1.12) ...
Esto confirma que ambos componentes pueden comunicarse correctamente.
4.2 Listar imágenes disponibles
Antes de ejecutar contenedores, es útil saber qué imágenes tienes disponibles localmente.
docker images
O su forma más moderna y explícita:
docker image ls
Salida esperada:
REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 605c77e624dd 2 weeks ago 141MB node 20 e6f4f87f1032 3 weeks ago 910MB hello-world latest feb5d9fea6a5 2 months ago 13.3kB
Este comando muestra:
- REPOSITORY: Nombre de la imagen
- TAG: Versión o etiqueta de la imagen (por defecto
latest) - IMAGE ID: Identificador único de la imagen
- CREATED: Cuándo fue creada la imagen
- SIZE: Tamaño de la imagen
4.3 Descargar imágenes desde Docker Hub
Docker Hub es el registro público por defecto de Docker. Para descargar una imagen sin ejecutarla inmediatamente, se utiliza el comando docker pull.
docker pull nginx
Esto descarga la imagen oficial de Nginx (con la etiqueta latest por defecto) y la deja disponible localmente para futuros contenedores.
También puedes descargar versiones específicas usando etiquetas:
docker pull node:20 docker pull postgres:15-alpine docker pull python:3.11-slim
Esto es fundamental para garantizar consistencia entre entornos y evitar cambios inesperados por actualizaciones automáticas.
Descargar una imagen específica de una plataforma:
Si trabajas con Apple Silicon (M1/M2/M3) o necesitas una arquitectura específica:
docker pull --platform linux/amd64 nginx
4.4 Ejecutar un contenedor
Ejecutar un contenedor es una de las acciones más comunes en Docker. El comando principal para hacerlo es docker run.
Ejemplo básico:
docker run hello-world
Este comando realiza varias acciones automáticamente:
- Busca la imagen
hello-worldlocalmente - Si no existe, la descarga desde Docker Hub
- Crea un contenedor basado en la imagen
- Ejecuta el contenedor
- Muestra su salida
- El contenedor se detiene automáticamente
Ejecutar un servidor web en segundo plano:
En aplicaciones reales, normalmente ejecutarás contenedores que se mantienen activos:
docker run -d -p 8080:80 nginx
Explicación de los flags:
-d(detached): Ejecuta el contenedor en segundo plano y devuelve el control de la terminal-p 8080:80(port): Mapea el puerto 80 del contenedor al puerto 8080 del host- Formato:
-p [puerto_host]:[puerto_contenedor]
- Formato:
Ahora puedes acceder a Nginx desde tu navegador en http://localhost:8080
Opciones adicionales útiles:
docker run -d \ --name mi-nginx \ -p 8080:80 \ -v /ruta/local:/usr/share/nginx/html \ nginx
Flags explicados:
--name mi-nginx: Asigna un nombre personalizado al contenedor (facilita su gestión)-v(volume): Monta un directorio local dentro del contenedor-e: Define variables de entorno (ejemplo:-e MYSQL_ROOT_PASSWORD=secret)--rm: Elimina automáticamente el contenedor cuando se detiene-it: Modo interactivo con terminal (útil para shells)
Ejemplo con variables de entorno:
docker run -d \ --name mi-mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=mipassword \ -e MYSQL_DATABASE=miapp \ mysql:8
4.5 Listar contenedores
A medida que trabajas con Docker, crearás múltiples contenedores. Para ver cuáles están activos:
docker ps
Salida esperada:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp mi-nginx
Este comando muestra únicamente los contenedores que están en ejecución.
Para ver todos los contenedores, incluidos los detenidos:
docker ps -a
Esto muestra también contenedores con estado Exited, útil cuando necesitas limpiar contenedores antiguos o revisar contenedores que fallaron al iniciar.
Mostrar solo los IDs de los contenedores:
docker ps -q docker ps -aq # Todos los IDs, incluidos detenidos
Esto es especialmente útil para scripts y comandos en batch.
4.6 Gestionar el ciclo de vida de contenedores
Detener un contenedor
Cuando un contenedor está en ejecución, puedes detenerlo de forma controlada usando docker stop:
docker stop mi-nginx # O usando el ID del contenedor docker stop a1b2c3d4e5f6
Docker envía una señal SIGTERM al proceso principal del contenedor, permitiendo que la aplicación se cierre correctamente (guarde estado, cierre conexiones, etc.). Si después de 10 segundos el contenedor no se ha detenido, Docker envía un SIGKILL forzado.
Para forzar la detención inmediata:
docker kill mi-nginx
Esto envía directamente SIGKILL, terminando el contenedor sin darle tiempo de limpieza. Úsalo solo cuando docker stop no funcione.
Iniciar un contenedor detenido
Si un contenedor fue detenido pero no eliminado, puedes reiniciarlo:
docker start mi-nginx
Esto mantiene todas las configuraciones, volúmenes y estado del contenedor.
Reiniciar un contenedor
Para detener y volver a iniciar un contenedor:
docker restart mi-nginx
Útil cuando necesitas aplicar cambios de configuración o resolver problemas temporales.
4.7 Eliminar contenedores
Una vez que un contenedor ya no es necesario, puedes eliminarlo para liberar recursos.
docker rm mi-nginx
Si el contenedor está en ejecución, primero debe detenerse, o usar el flag -f (force):
docker rm -f mi-nginx
Eliminar varios contenedores a la vez:
docker rm contenedor1 contenedor2 contenedor3
Eliminar todos los contenedores detenidos:
docker container prune
Docker te pedirá confirmación antes de eliminarlos. Para omitir la confirmación:
docker container prune -f
Eliminar automáticamente un contenedor al detenerse:
Cuando ejecutas un contenedor con el flag --rm, Docker lo elimina automáticamente cuando se detiene:
docker run --rm -it ubuntu bash
Esto es útil para contenedores de prueba o temporales.
4.8 Construir una imagen desde un Dockerfile
Cuando desarrollas tu propia aplicación, lo habitual es crear una imagen personalizada a partir de un Dockerfile.
El comando para construir una imagen es:
docker build -t mi-aplicacion:1.0 .
Explicación:
-t mi-aplicacion:1.0(tag): Asigna un nombre y etiqueta a la imagen- Formato:
nombre:version - Si omites la versión, se usa
latestpor defecto
- Formato:
.(punto): Indica el contexto de construcción, es decir, el directorio donde está el Dockerfile y los archivos necesarios
Docker lee el Dockerfile ubicado en el directorio actual, ejecuta cada instrucción línea por línea y genera una imagen lista para ser utilizada.
Especificar un Dockerfile con otro nombre o ubicación:
docker build -t mi-app -f Dockerfile.prod .
Construir sin usar caché (útil para forzar reconstrucción completa):
docker build --no-cache -t mi-app .
Una vez construida, puedes ejecutar un contenedor basado en esa imagen:
docker run -d -p 3000:3000 mi-aplicacion:1.0
Este flujo es la base de Docker en proyectos profesionales.
4.9 Gestionar imágenes
Ver información detallada de una imagen
docker inspect nginx
Esto muestra toda la metadata de la imagen en formato JSON: capas, variables de entorno, puertos expuestos, comandos, etc.
Eliminar imágenes
docker rmi nginx # O usando el IMAGE ID docker rmi 605c77e624dd
No puedes eliminar una imagen si hay contenedores (aunque estén detenidos) basados en ella. Primero debes eliminar los contenedores.
Forzar eliminación:
docker rmi -f nginx
Eliminar todas las imágenes sin usar:
docker image prune
Eliminar todas las imágenes (cuidado, esto elimina TODAS):
docker image prune -a
4.10 Ver logs de un contenedor
Cuando una aplicación dentro de un contenedor falla o se comporta de forma inesperada, los logs son la primera herramienta de diagnóstico.
docker logs mi-nginx
Este comando muestra la salida estándar (stdout) y errores (stderr) del contenedor, equivalente a lo que verías en la consola si la aplicación se ejecutara directamente.
Seguir los logs en tiempo real (como tail -f):
docker logs -f mi-nginx
Esto es especialmente útil para servidores, APIs y aplicaciones en ejecución continua. Presiona Ctrl+C para salir.
Mostrar solo las últimas N líneas:
docker logs --tail 50 mi-nginx
Mostrar logs con timestamps:
docker logs -t mi-nginx
Combinación útil (últimas 100 líneas en tiempo real con timestamps):
docker logs -f --tail 100 -t mi-nginx
4.11 Ejecutar comandos dentro de un contenedor
En ocasiones necesitas acceder al interior de un contenedor para inspeccionar archivos, ejecutar comandos o depurar un problema.
Abrir una terminal interactiva
docker exec -it mi-nginx bash
Explicación:
exec: Ejecuta un comando en un contenedor en ejecución-i(interactive): Mantiene STDIN abierto-t(tty): Asigna una pseudo-terminalbash: El comando a ejecutar (en este caso, una shell)
En imágenes basadas en Alpine Linux u otras ligeras, usa sh en lugar de bash:
docker exec -it mi-contenedor sh
Nota importante: Algunos contenedores minimalistas (como scratch o contenedores con solo el binario de la aplicación) no tienen ningún shell instalado, por lo que no podrás acceder de esta manera.
Ejecutar un comando sin entrar al contenedor
docker exec mi-nginx ls -la /usr/share/nginx/html docker exec mi-nginx cat /etc/nginx/nginx.conf
Ver procesos en ejecución dentro del contenedor
docker top mi-nginx
Obtener estadísticas en tiempo real
docker stats mi-nginx
Muestra uso de CPU, memoria, red y disco del contenedor en tiempo real.
4.12 Inspeccionar contenedores e imágenes
Para obtener información técnica detallada:
docker inspect mi-nginx
Esto devuelve un JSON con toda la configuración: red, volúmenes, variables de entorno, estado, etc.
Filtrar información específica con formato:
docker inspect --format='{{.NetworkSettings.IPAddress}}' mi-nginx
4.13 Copiar archivos entre host y contenedor
Copiar desde el host al contenedor:
docker cp archivo.txt mi-nginx:/usr/share/nginx/html/
Copiar desde el contenedor al host:
docker cp mi-nginx:/var/log/nginx/access.log ./logs/
4.14 Limpieza general del sistema Docker
Con el tiempo, Docker acumula imágenes, contenedores, volúmenes y redes sin usar que ocupan espacio.
Limpiar todo lo no utilizado (contenedores detenidos, redes sin usar, imágenes dangling, caché de build):
docker system prune
Limpieza agresiva (incluye todas las imágenes sin contenedores asociados):
docker system prune -a
Ver cuánto espacio está usando Docker:
docker system df
Resumen práctico
Con estos comandos ya puedes:
✅ Descargar y gestionar imágenes
✅ Ejecutar, detener y reiniciar contenedores
✅ Asignar puertos y variables de entorno
✅ Ver logs y depurar aplicaciones
✅ Entrar a contenedores en ejecución
✅ Construir imágenes personalizadas
✅ Limpiar recursos no utilizados
✅ Inspeccionar y monitorear el estado del sistema Docker
Dominar estos comandos te da el ochenta por ciento del poder práctico de Docker en el día a día. A partir de aquí, el siguiente paso es aprender a crear tus propias imágenes con Dockerfiles, trabajar con volúmenes persistentes y orquestar múltiples contenedores con Docker Compose.
5. Dockerfile: Estructura y Buenas Prácticas
El Dockerfile es el corazón de cualquier proyecto Docker serio. Si los contenedores son lo que se ejecuta y las imágenes lo que se distribuye, el Dockerfile es el documento que define exactamente cómo se construye todo.
Un buen Dockerfile hace que una aplicación sea reproducible, segura, eficiente y fácil de mantener. Un mal Dockerfile, en cambio, genera imágenes pesadas, lentas, inseguras y difíciles de depurar.
En esta sección aprenderás cómo funciona un Dockerfile, cuáles son sus instrucciones principales y qué buenas prácticas seguir para crear imágenes profesionales.
5.1 ¿Qué es un Dockerfile?
Un Dockerfile es un archivo de texto plano que contiene una serie de instrucciones que Docker ejecuta de forma secuencial para construir una imagen.
Cada línea del Dockerfile describe un paso concreto del proceso de construcción: qué sistema base usar, qué dependencias instalar, qué archivos copiar y cómo debe iniciarse la aplicación.
Gracias al Dockerfile:
- La construcción de imágenes es automática
- El resultado es siempre el mismo
- El proceso puede versionarse junto al código
- El entorno queda documentado de forma explícita
En otras palabras, el Dockerfile elimina la improvisación y convierte el entorno en parte del proyecto.
5.2 Estructura básica de un Dockerfile
Un Dockerfile sigue una estructura lógica y predecible. Aunque puede variar según el proyecto, la mayoría incluye estos bloques:
- Argumentos de construcción (opcional)
- Imagen base
- Metadata (opcional)
- Instalación de dependencias del sistema
- Configuración del directorio de trabajo
- Copia de archivos de dependencias
- Instalación de dependencias de la aplicación
- Copia del código fuente
- Configuración del entorno
- Exposición de puertos
- Usuario de ejecución (recomendado)
- Health check (opcional)
- Comando de arranque
Docker lee el Dockerfile de arriba hacia abajo, ejecutando cada instrucción y generando una nueva capa de imagen en cada paso.
El orden de las instrucciones es crucial tanto para el funcionamiento como para aprovechar eficientemente la caché de Docker y reducir tiempos de construcción.
5.3 Tabla de referencia de instrucciones
| Instrucción | Descripción | Ejemplo |
|---|---|---|
FROM | Define la imagen base | FROM node:20-alpine |
ARG | Define argumentos de construcción | ARG NODE_VERSION=20 |
LABEL | Añade metadata a la imagen | LABEL maintainer="dev@example.com" |
WORKDIR | Establece el directorio de trabajo | WORKDIR /app |
COPY | Copia archivos al contenedor | COPY package.json . |
ADD | Copia archivos con funciones extras | ADD archivo.tar.gz /app/ |
RUN | Ejecuta comandos durante la construcción | RUN npm install |
ENV | Define variables de entorno | ENV NODE_ENV=production |
EXPOSE | Documenta puertos expuestos | EXPOSE 3000 |
USER | Cambia el usuario de ejecución | USER node |
VOLUME | Define puntos de montaje | VOLUME /data |
HEALTHCHECK | Define verificación de salud | HEALTHCHECK CMD curl -f http://localhost/ |
CMD | Comando por defecto (sobrescribible) | CMD ["node", "app.js"] |
ENTRYPOINT | Comando principal (no sobrescribible) | ENTRYPOINT ["node"] |
5.4 Instrucciones principales del Dockerfile
5.4.1 FROM – Imagen base
Define la imagen base sobre la que se construirá la nueva imagen.
FROM node:20-alpine
Debe ser la primera instrucción del Dockerfile (excepto si hay ARG que se use en FROM).
Elegir una buena imagen base es crucial para el tamaño, seguridad y rendimiento final.
Buenas prácticas:
✅ Preferir imágenes oficiales verificadas
✅ Usar versiones específicas (no latest)
✅ Elegir variantes ligeras como alpine o slim cuando sea posible
✅ Considerar imágenes distroless para máxima seguridad
Ejemplos:
# Malo: versión imprecisa FROM node # Mejor: versión específica FROM node:20 # Óptimo: versión específica y ligera FROM node:20-alpine # Para Python FROM python:3.11-slim # Para Go (multi-stage) FROM golang:1.21-alpine AS builder
5.4.2 ARG – Argumentos de construcción
Define variables que solo existen durante la construcción de la imagen.
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine
ARG APP_VERSION=1.0.0
LABEL version="${APP_VERSION}"
Diferencia con ENV:
ARG: solo disponible durante el buildENV: disponible durante el build Y en el contenedor en ejecución
Pasar argumentos al construir:
docker build --build-arg NODE_VERSION=18 -t mi-app .
5.4.3 LABEL – Metadata
Añade información descriptiva a la imagen.
LABEL maintainer="desarrollador@ejemplo.com" LABEL version="1.0.0" LABEL description="API REST para gestión de usuarios"
Útil para documentación, automatización y organización de imágenes.
5.4.4 WORKDIR – Directorio de trabajo
Establece el directorio de trabajo para las instrucciones siguientes.
WORKDIR /app
Ventajas sobre RUN cd /app:
- Crea el directorio si no existe
- Más limpio y explícito
- Afecta a todas las instrucciones siguientes
Buena práctica: Usar rutas absolutas y evitar /root como directorio de trabajo.
WORKDIR /app COPY package.json . RUN npm install COPY . .
5.4.5 COPY vs ADD – Copiar archivos
COPY copia archivos o directorios desde el host al contenedor.
COPY package.json /app/ COPY src/ /app/src/ COPY . .
ADD tiene las mismas capacidades que COPY, pero con funcionalidades adicionales:
- Descomprime automáticamente archivos
.tar,.tar.gz, etc. - Puede descargar archivos desde URLs
ADD archivo.tar.gz /app/ ADD https://ejemplo.com/archivo.txt /app/
Buena práctica: Usar COPY siempre que sea posible y reservar ADD solo cuando necesites explícitamente sus funcionalidades extras. COPY es más predecible.
Copiado eficiente para aprovechar caché:
# ❌ Malo: copia todo de una vez COPY . . RUN npm install # ✅ Bueno: copia dependencias primero COPY package*.json ./ RUN npm install COPY . .
5.4.6 RUN – Ejecutar comandos
Ejecuta comandos durante la construcción de la imagen.
RUN npm install RUN apt-get update && apt-get install -y curl
Cada RUN crea una nueva capa en la imagen, por lo que conviene agrupar comandos relacionados para reducir el número de capas y el tamaño final.
❌ Malo: múltiples capas innecesarias
RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y git RUN rm -rf /var/lib/apt/lists/*
✅ Bueno: agrupado en una sola capa
RUN apt-get update && \
apt-get install -y \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
Explicación:
&&encadena comandos (solo ejecuta el siguiente si el anterior tuvo éxito)\continúa la instrucción en la siguiente línea- Limpiar caché de paquetes en la misma capa reduce el tamaño
Buenas prácticas:
✅ Ordenar paquetes alfabéticamente para mejor legibilidad
✅ Limpiar cachés y archivos temporales en la misma capa
✅ Usar --no-cache o equivalente al instalar paquetes
✅ Verificar firma de paquetes cuando sea crítico
5.4.7 ENV – Variables de entorno
Define variables de entorno dentro del contenedor.
ENV NODE_ENV=production ENV PORT=3000 ENV DATABASE_URL=postgres://localhost:5432/mydb
Las variables persisten en el contenedor en ejecución, a diferencia de ARG.
Pueden sobrescribirse al ejecutar el contenedor:
docker run -e NODE_ENV=development mi-app
Buena práctica: Usar variables de entorno para configuración que puede cambiar entre entornos (desarrollo, staging, producción).
5.4.8 EXPOSE – Documentar puertos
Documenta qué puerto utiliza la aplicación dentro del contenedor.
EXPOSE 3000 EXPOSE 8080
Importante: EXPOSE NO abre el puerto automáticamente. Solo sirve como documentación y para herramientas de orquestación.
Para realmente exponer el puerto, usa -p al ejecutar:
docker run -p 3000:3000 mi-app
5.4.9 USER – Usuario de ejecución
Cambia el usuario con el que se ejecutan las instrucciones siguientes y el contenedor.
# Crear usuario no privilegiado
RUN addgroup -g 1001 appgroup && \
adduser -D -u 1001 -G appgroup appuser
# Cambiar propiedad de archivos
RUN chown -R appuser:appgroup /app
# Cambiar al usuario no root
USER appuser
⚠️ Importante para seguridad: Por defecto, los contenedores se ejecutan como root. Esto es un riesgo de seguridad significativo.
Buena práctica: Siempre cambiar a un usuario no privilegiado antes de ejecutar la aplicación.
Muchas imágenes oficiales ya incluyen usuarios no root:
# Node.js incluye el usuario 'node' FROM node:20-alpine USER node
5.4.10 VOLUME – Puntos de montaje
Define directorios que deben persistir o compartirse.
VOLUME /data VOLUME /var/log/app
Los volúmenes permiten que los datos persistan incluso cuando el contenedor se elimina.
Generalmente, es mejor definir volúmenes al ejecutar el contenedor:
docker run -v /ruta/host:/data mi-app
5.4.11 HEALTHCHECK – Verificación de salud
Define un comando para verificar si el contenedor está saludable.
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1
Parámetros:
--interval: Cada cuánto ejecutar el check--timeout: Tiempo máximo de espera--start-period: Tiempo de gracia al iniciar--retries: Intentos antes de marcar como unhealthy
Docker usa esta información para monitorear el estado del contenedor y puede reiniciarlo automáticamente si falla.
5.4.12 CMD – Comando por defecto
Define el comando por defecto que se ejecuta cuando el contenedor se inicia.
CMD ["node", "app.js"]
Formatos:
# Formato exec (recomendado) CMD ["executable", "param1", "param2"] # Formato shell CMD executable param1 param2
CMD puede ser sobrescrito al ejecutar el contenedor:
docker run mi-app node debug.js
5.4.13 ENTRYPOINT – Comando principal
Define el comando principal e inmutable del contenedor.
ENTRYPOINT ["node"]
A diferencia de CMD, ENTRYPOINT no se sobrescribe fácilmente. Se usa cuando el contenedor debe comportarse como un ejecutable específico.
Combinación de ENTRYPOINT y CMD:
Una práctica común es usar ENTRYPOINT para el comando base y CMD para los argumentos por defecto:
ENTRYPOINT ["node"] CMD ["app.js"]
Esto permite ejecutar:
# Ejecuta: node app.js (por defecto) docker run mi-app # Ejecuta: node debug.js (sobrescribe CMD) docker run mi-app debug.js # Ejecuta: node --inspect app.js (añade flags) docker run mi-app --inspect app.js
Cuándo usar cada uno:
| Situación | Usar |
|---|---|
| Aplicación flexible que puede cambiar | CMD |
| Contenedor que actúa como herramienta/comando fijo | ENTRYPOINT |
| Comando base fijo + argumentos variables | ENTRYPOINT + CMD |
5.5 Archivo .dockerignore
El archivo .dockerignore funciona de manera similar a .gitignore. Indica qué archivos no deben copiarse al contexto de construcción.
Esto es fundamental para:
✅ Reducir el tamaño de la imagen
✅ Acelerar la construcción
✅ Evitar copiar archivos sensibles
✅ Prevenir invalidación innecesaria de caché
Ejemplo completo de .dockerignore:
# Dependencias node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* # Archivos de build dist/ build/ *.pyc __pycache__/ # Control de versiones .git/ .gitignore .gitattributes # IDEs .vscode/ .idea/ *.swp *.swo *~ # Sistema operativo .DS_Store Thumbs.db # Docker Dockerfile .dockerignore docker-compose*.yml # Archivos sensibles .env .env.local *.key *.pem secrets/ # Documentación README.md docs/ *.md # Tests tests/ test/ *.test.js *.spec.js coverage/ # CI/CD .github/ .gitlab-ci.yml .travis.yml # Logs logs/ *.log
⚠️ Usar .dockerignore NO es opcional en proyectos reales: es una buena práctica esencial.
5.6 Multi-stage builds (construcciones en varias etapas)
Las multi-stage builds permiten usar varias imágenes base dentro de un mismo Dockerfile.
La idea es simple: usar una imagen completa para construir la aplicación y una imagen minimalista para ejecutarla.
Ventajas:
✅ Reduce drásticamente el tamaño final de la imagen
✅ Mejora la seguridad (menos paquetes = menos vulnerabilidades)
✅ Separa herramientas de build de runtime
✅ Imágenes de producción más limpias
Sintaxis:
# Etapa 1: Construcción (nombrada con AS) FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Etapa 2: Producción (imagen final) FROM node:20-alpine WORKDIR /app # Copiar solo lo necesario desde la etapa anterior COPY --from=builder /app/dist ./dist COPY --from=builder /app/package*.json ./ RUN npm install --omit=dev CMD ["node", "dist/app.js"]
Explicación:
- Primera etapa (
AS builder): Usa todas las herramientas necesarias para construir (compiladores, build tools, dependencias de desarrollo) - Segunda etapa: Parte de una imagen limpia y copia SOLO los artefactos construidos
COPY --from=builder: Copia archivos desde una etapa anterior
Comparación de tamaños:
- Sin multi-stage: 1.2 GB (incluye node_modules completo, código fuente, herramientas de build)
- Con multi-stage: 150 MB (solo runtime y archivos compilados)
Esta técnica es estándar en aplicaciones Node.js, Go, Java, React y Angular.
5.7 El sistema de caché de Docker
Docker cachea cada capa de la imagen. Si una instrucción no ha cambiado, Docker reutiliza la capa cacheada en lugar de ejecutarla nuevamente.
Cómo funciona:
- Docker ejecuta cada instrucción
- Compara el resultado con capas cacheadas previas
- Si coincide, usa la caché (muy rápido)
- Si cambia, ejecuta la instrucción y invalida la caché de todas las capas siguientes
Por eso el orden importa:
❌ Malo: invalida caché con cada cambio de código
COPY . . RUN npm install
Si cambias cualquier archivo, se invalida COPY . . y también RUN npm install, reinstalando todas las dependencias.
✅ Bueno: aprovecha caché al máximo
COPY package*.json ./ RUN npm install COPY . .
Si cambias código pero no package.json, Docker reutiliza la capa de npm install.
Principio general: Copiar primero lo que cambia menos frecuentemente.
5.8 Buenas prácticas esenciales
1. Usar imágenes base oficiales y específicas
# ❌ Malo FROM node # ✅ Bueno FROM node:20-alpine
2. Minimizar el número de capas
# ❌ Malo: 3 capas
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# ✅ Bueno: 1 capa
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
3. Ordenar instrucciones de menos a más cambiante
FROM node:20-alpine WORKDIR /app # 1. Dependencias (cambian poco) COPY package*.json ./ RUN npm install # 2. Código fuente (cambia frecuentemente) COPY . . CMD ["node", "app.js"]
4. No ejecutar como root
USER node
5. Usar .dockerignore
Siempre crear y mantener actualizado el .dockerignore.
6. Multi-stage builds para producción
Especialmente importante en Node.js, Go, Java, frontend builds.
7. Escanear vulnerabilidades
docker scan mi-imagen:latest
O usar herramientas como Trivy, Snyk, Clair.
8. No incluir secretos en la imagen
# ❌ NUNCA HAGAS ESTO COPY .env . ENV API_KEY=secreto123
Usa variables de entorno al ejecutar o servicios de secretos.
9. Limpiar en la misma capa
RUN apt-get update && \
apt-get install -y package && \
rm -rf /var/lib/apt/lists/*
10. Documentar con LABEL
LABEL maintainer="equipo@ejemplo.com" LABEL version="2.1.0" LABEL description="API de autenticación"
5.9 Ejemplos completos por lenguaje
Node.js (con multi-stage)
# Etapa de construcción
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Etapa de producción
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev && \
npm cache clean --force
USER node
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
CMD ["node", "dist/app.js"]
Python
FROM python:3.11-slim
WORKDIR /app
# Instalar dependencias del sistema si es necesario
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
rm -rf /var/lib/apt/lists/*
# Copiar solo requirements primero
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copiar código
COPY . .
# Crear usuario no root
RUN useradd -m -u 1000 appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]
Go (multi-stage extremo)
# Etapa de construcción FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o app . # Etapa de producción (imagen mínima) FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/app . EXPOSE 8080 CMD ["./app"]
React/Frontend (build estático)
# Etapa de construcción FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Etapa de producción con Nginx FROM nginx:alpine COPY --from=builder /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
Dominar Dockerfile es dar el salto de «usar Docker» a diseñar imágenes profesionales.
Un Dockerfile bien escrito mejora:
✅ Rendimiento: construcciones más rápidas con caché optimizado
✅ Seguridad: menos superficie de ataque, sin root, sin secretos
✅ Mantenibilidad: código claro, versionable y documentado
✅ Escalabilidad: imágenes pequeñas y eficientes
✅ Costos: menos almacenamiento, menos transferencia de red
Los principios fundamentales son:
- Usar imágenes base oficiales y ligeras
- Aprovechar el sistema de caché
- Aplicar multi-stage builds
- No ejecutar como root
- Usar .dockerignore siempre
- Limpiar en la misma capa
- Documentar y versionar
Con estos conocimientos estás listo para crear Dockerfiles profesionales que funcionan bien en desarrollo y producción.
6. Volúmenes en Docker
Por diseño, los contenedores Docker son efímeros. Esto significa que cuando un contenedor se elimina, todos los datos que se hayan generado dentro de él desaparecen. Esta característica es ideal para la portabilidad, pero plantea un problema importante: ¿qué ocurre con los datos que deben persistir, como bases de datos, archivos subidos por usuarios o logs?
Para resolver esto, Docker introduce el concepto de volúmenes, un mecanismo que permite almacenar datos fuera del ciclo de vida del contenedor y reutilizarlos de forma segura y controlada.
6.1 ¿Qué son los volúmenes en Docker?
Un volumen en Docker es un mecanismo de almacenamiento persistente gestionado directamente por Docker. A diferencia del sistema de archivos interno del contenedor, los datos almacenados en un volumen no se eliminan cuando el contenedor se detiene o se borra.
Los volúmenes viven en el host (generalmente en /var/lib/docker/volumes/ en Linux), pero Docker se encarga completamente de su ubicación, permisos y gestión. Desde el punto de vista del contenedor, el volumen se comporta como un directorio normal, lo que permite a la aplicación leer y escribir datos sin saber dónde se almacenan realmente.
Ventajas de los volúmenes:
✅ Persistencia: Los datos sobreviven a la eliminación del contenedor
✅ Portabilidad: Independientes de la estructura del host
✅ Rendimiento: Mejor performance que bind mounts en Windows/Mac
✅ Gestión: Comandos dedicados para backup, migración y limpieza
✅ Compartición: Múltiples contenedores pueden usar el mismo volumen
✅ Seguridad: Aislamiento del sistema de archivos del host
Gracias a los volúmenes, Docker puede utilizarse de forma fiable para aplicaciones que manejan estado, algo imprescindible en entornos de producción.
6.2 Tipos de almacenamiento en Docker
Docker ofrece tres formas principales de manejar almacenamiento persistente, cada una pensada para distintos escenarios:
| Tipo | Descripción | Gestión | Uso principal |
|---|---|---|---|
| Volumes | Gestionados por Docker | Docker | Producción – Datos persistentes |
| Bind Mounts | Directorio del host | Usuario | Desarrollo – Código en tiempo real |
| tmpfs | Memoria RAM | Temporal | Datos sensibles/temporales |
6.2.1 Volumes (Volúmenes)
El enfoque recomendado por Docker. Son portables, seguros y desacoplados de la estructura del sistema anfitrión.
Host: /var/lib/docker/volumes/mi-volumen/_data
↓ (montado en)
Contenedor: /app/data
Cuándo usar:
- Bases de datos
- Datos de producción
- Archivos subidos por usuarios
- Cuando necesitas portabilidad entre entornos
6.2.2 Bind Mounts
Montan un directorio específico del host dentro del contenedor. Muy útiles en desarrollo, pero menos portables.
Host: /home/usuario/proyecto/src
↓ (montado en)
Contenedor: /app/src
Cuándo usar:
- Desarrollo local
- Hot reload de código
- Compartir configuración del host
- Acceso a archivos específicos del sistema
6.2.3 tmpfs Mounts
Almacenamiento en memoria RAM. Los datos se pierden al detener el contenedor.
Cuándo usar:
- Datos sensibles que no deben persistir (tokens, contraseñas temporales)
- Cache temporal de alto rendimiento
- Datos que no necesitan persistir
Recomendación general:
- Volúmenes para producción
- Bind mounts para desarrollo
- tmpfs para datos temporales sensibles
6.3 Trabajar con volúmenes
6.3.1 Crear un volumen
Para crear un volumen manualmente:
docker volume create mi-volumen
Salida esperada:
mi-volumen
Docker registra el volumen y lo mantiene disponible incluso si no hay contenedores usándolo.
Crear volumen con driver específico:
docker volume create --driver local \ --opt type=nfs \ --opt o=addr=192.168.1.1,rw \ --opt device=:/path/to/dir \ mi-volumen-nfs
6.3.2 Listar volúmenes
Para ver todos los volúmenes existentes:
docker volume ls
Salida esperada:
DRIVER VOLUME NAME local mi-volumen local postgres-data local abc123def456 # Volumen anónimo
Filtrar volúmenes:
# Ver solo volúmenes huérfanos (sin contenedores asociados) docker volume ls -f dangling=true
6.3.3 Inspeccionar un volumen
Para ver información detallada de un volumen:
docker volume inspect mi-volumen
Salida esperada:
[
{
"CreatedAt": "2024-01-27T10:30:00Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/mi-volumen/_data",
"Name": "mi-volumen",
"Options": {},
"Scope": "local"
}
]
El campo Mountpoint muestra dónde está físicamente en el host.
6.3.4 Eliminar volúmenes
Eliminar un volumen específico:
docker volume rm mi-volumen
⚠️ No puedes eliminar un volumen que esté en uso por un contenedor (aunque esté detenido).
Forzar eliminación (primero elimina el contenedor):
docker rm -f contenedor docker volume rm mi-volumen
Eliminar todos los volúmenes no utilizados:
docker volume prune
Docker pedirá confirmación. Para omitirla:
docker volume prune -f
6.4 Montar volúmenes en contenedores
Existen dos sintaxis para montar volúmenes: -v (legacy, más simple) y --mount (moderna, más explícita).
6.4.1 Sintaxis con -v (volúmenes nombrados)
docker run -d \ --name mi-app \ -v mi-volumen:/app/data \ nginx
Formato: -v [nombre-volumen]:[ruta-en-contenedor]:[opciones]
Si el volumen no existe, Docker lo crea automáticamente.
Opciones comunes:
ro(read-only): Solo lecturarw(read-write): Lectura y escritura (por defecto)
docker run -v mi-volumen:/app/data:ro nginx
6.4.2 Sintaxis con –mount (recomendada)
docker run -d \ --name mi-app \ --mount source=mi-volumen,target=/app/data \ nginx
Ventajas de –mount:
- Más explícito y legible
- Mejor manejo de errores
- Soporta más opciones avanzadas
Opciones adicionales:
docker run -d \ --mount source=mi-volumen,target=/app/data,readonly \ nginx
6.4.3 Volúmenes anónimos
Si no especificas un nombre, Docker crea un volumen anónimo:
docker run -v /app/data nginx
Docker generará un ID aleatorio para el volumen (ej: abc123def456).
Problema: Difíciles de identificar y gestionar.
Solución: Siempre usar volúmenes nombrados en producción.
6.5 Bind Mounts (montajes del host)
Los bind mounts conectan un directorio específico del host con el contenedor.
6.5.1 Sintaxis con -v
docker run -d \ -v /ruta/absoluta/en/host:/app/src \ node:20-alpine
⚠️ La ruta del host debe ser absoluta.
6.5.2 Sintaxis con –mount
docker run -d \ --mount type=bind,source=/home/usuario/proyecto,target=/app \ node:20-alpine
Si la ruta no existe, –mount falla (más seguro).
Con -v, Docker crea el directorio automáticamente.
6.5.3 Ejemplo práctico: Desarrollo con hot reload
# Estructura del proyecto /home/usuario/mi-app/ ├── src/ ├── package.json └── Dockerfile # Ejecutar con bind mount para desarrollo docker run -d \ --name dev-app \ -p 3000:3000 \ -v /home/usuario/mi-app/src:/app/src \ -v /home/usuario/mi-app/package.json:/app/package.json \ mi-app:dev
Ahora puedes editar archivos en tu editor y ver los cambios reflejados inmediatamente en el contenedor (con hot reload configurado).
6.6 tmpfs Mounts (almacenamiento en memoria)
Los tmpfs mounts almacenan datos en la memoria RAM del host. Los datos se pierden al detener el contenedor.
docker run -d \ --tmpfs /tmp:rw,size=100m,mode=1777 \ nginx
Opciones:
size: Tamaño máximomode: Permisos (en octal)
Uso con –mount:
docker run -d \ --mount type=tmpfs,target=/tmp,tmpfs-size=100m \ nginx
Casos de uso:
- Almacenar tokens temporales
- Cache de sesiones
- Archivos temporales sensibles
6.7 Volúmenes vs Bind Mounts: Comparación detallada
| Característica | Volumes | Bind Mounts |
|---|---|---|
| Gestión | Docker | Usuario |
| Ubicación | /var/lib/docker/volumes/ | Cualquier ruta del host |
| Portabilidad | ✅ Alta | ❌ Baja |
| Rendimiento (Win/Mac) | ✅ Mejor | ⚠️ Más lento |
| Backup | ✅ Fácil | ⚠️ Manual |
| Seguridad | ✅ Aislado | ⚠️ Acceso directo al host |
| Uso en producción | ✅ Recomendado | ❌ No recomendado |
| Uso en desarrollo | ✅ Posible | ✅ Ideal |
| Auto-creación | ✅ Sí | ⚠️ Depende (-v sí, –mount no) |
6.8 Casos de uso prácticos con ejemplos
6.8.1 Base de datos MySQL con volumen persistente
# Crear volumen para MySQL docker volume create mysql-data # Ejecutar MySQL con volumen docker run -d \ --name mysql-server \ -e MYSQL_ROOT_PASSWORD=mipassword \ -e MYSQL_DATABASE=miapp \ -v mysql-data:/var/lib/mysql \ -p 3306:3306 \ mysql:8 # Los datos persisten aunque elimines el contenedor docker rm -f mysql-server # Recrear el contenedor con los mismos datos docker run -d \ --name mysql-server \ -e MYSQL_ROOT_PASSWORD=mipassword \ -v mysql-data:/var/lib/mysql \ -p 3306:3306 \ mysql:8
6.8.2 PostgreSQL con volumen
docker volume create postgres-data docker run -d \ --name postgres-server \ -e POSTGRES_PASSWORD=mipassword \ -e POSTGRES_DB=miapp \ -v postgres-data:/var/lib/postgresql/data \ -p 5432:5432 \ postgres:15
6.8.3 MongoDB con volumen
docker volume create mongo-data docker run -d \ --name mongo-server \ -v mongo-data:/data/db \ -p 27017:27017 \ mongo:7
6.8.4 Aplicación web con archivos subidos por usuarios
docker volume create uploads-data docker run -d \ --name web-app \ -v uploads-data:/app/uploads \ -p 8080:8080 \ mi-aplicacion:latest
6.8.5 Logs persistentes
docker volume create app-logs docker run -d \ --name mi-servicio \ -v app-logs:/var/log/app \ mi-servicio:latest # Acceder a los logs desde otro contenedor docker run --rm \ -v app-logs:/logs \ alpine cat /logs/app.log
6.8.6 Compartir volumen entre múltiples contenedores
# Crear volumen compartido docker volume create shared-data # Contenedor 1: escribe datos docker run -d \ --name writer \ -v shared-data:/data \ alpine sh -c "while true; do echo \$(date) >> /data/log.txt; sleep 5; done" # Contenedor 2: lee datos docker run -d \ --name reader \ -v shared-data:/data:ro \ alpine sh -c "while true; do tail -f /data/log.txt; sleep 10; done"
6.9 Volúmenes en Dockerfile
Puedes declarar volúmenes directamente en el Dockerfile usando la instrucción VOLUME:
FROM postgres:15 # Declarar volumen VOLUME /var/lib/postgresql/data CMD ["postgres"]
¿Qué hace esto?
- Documenta qué directorios deben persistir
- Docker creará un volumen anónimo automáticamente si no se especifica uno al ejecutar
Ejemplo de uso:
# Docker creará un volumen anónimo automáticamente docker run -d postgres:15 # Mejor: especificar volumen nombrado docker run -d -v postgres-data:/var/lib/postgresql/data postgres:15
Recomendación: Usar VOLUME en el Dockerfile para documentación, pero siempre especificar volúmenes nombrados al ejecutar contenedores en producción.
6.10 Backup y restore de volúmenes
6.10.1 Hacer backup de un volumen
# Crear un backup del volumen mysql-data docker run --rm \ -v mysql-data:/data \ -v $(pwd):/backup \ alpine tar czf /backup/mysql-backup.tar.gz -C /data .
Explicación:
- Montamos el volumen a respaldar en
/data - Montamos el directorio actual en
/backup - Creamos un archivo tar.gz con el contenido del volumen
- El flag
--rmelimina el contenedor temporal al terminar
6.10.2 Restaurar un volumen desde backup
# Crear nuevo volumen docker volume create mysql-data-restored # Restaurar desde backup docker run --rm \ -v mysql-data-restored:/data \ -v $(pwd):/backup \ alpine tar xzf /backup/mysql-backup.tar.gz -C /data
6.10.3 Copiar volumen completo a otro volumen
# Crear volumen de destino docker volume create mysql-data-copy # Copiar datos docker run --rm \ -v mysql-data:/source:ro \ -v mysql-data-copy:/dest \ alpine sh -c "cd /source && cp -av . /dest"
6.11 Permisos y ownership en volúmenes
Uno de los problemas más comunes con volúmenes es la incompatibilidad de permisos, especialmente en Linux.
Problema común:
docker run -d \ --name app \ -v app-data:/app/data \ mi-app:latest
El contenedor puede ejecutarse como usuario node (UID 1000), pero el volumen puede tener permisos de root.
Solución 1: Establecer ownership en el Dockerfile
FROM node:20-alpine
WORKDIR /app
# Crear directorio y establecer permisos
RUN mkdir -p /app/data && \
chown -R node:node /app
# Cambiar a usuario no privilegiado
USER node
VOLUME /app/data
Solución 2: Establecer permisos al crear el contenedor
docker run -d \ --name app \ --user 1000:1000 \ -v app-data:/app/data \ mi-app:latest
Solución 3: Usar un contenedor temporal para ajustar permisos
docker run --rm \ -v app-data:/data \ alpine chown -R 1000:1000 /data
6.12 Drivers de volúmenes
Docker soporta diferentes drivers para volúmenes, permitiendo almacenamiento en la nube, NFS, etc.
Drivers comunes:
| Driver | Descripción |
|---|---|
local | Almacenamiento local (por defecto) |
nfs | Network File System |
vieux/sshfs | SSH File System |
rexray/ebs | Amazon EBS |
cloudstor | Docker Cloud Storage |
Ejemplo con NFS:
docker volume create \ --driver local \ --opt type=nfs \ --opt o=addr=192.168.1.100,rw \ --opt device=:/path/to/share \ nfs-volume
Ejemplo con sshfs (plugin necesario):
# Instalar plugin docker plugin install vieux/sshfs # Crear volumen docker volume create \ --driver vieux/sshfs \ -o sshcmd=usuario@servidor:/ruta \ -o password=mipassword \ ssh-volume
6.13 Limpieza y mantenimiento
Ver uso de espacio de volúmenes
docker system df -v
Muestra cuánto espacio ocupan volúmenes, imágenes y contenedores.
Identificar volúmenes huérfanos
docker volume ls -f dangling=true
Eliminar volúmenes huérfanos
docker volume prune
Eliminar TODO (⚠️ peligroso)
docker system prune -a --volumes
Esto elimina:
- Contenedores detenidos
- Redes no utilizadas
- Imágenes sin contenedores
- TODOS los volúmenes no utilizados
6.14 Troubleshooting común
Problema 1: «No such volume»
Síntoma:
Error response from daemon: no such volume: mi-volumen
Solución:
# Verificar que el volumen existe docker volume ls | grep mi-volumen # Si no existe, créalo docker volume create mi-volumen
Problema 2: «Permission denied» al escribir en volumen
Síntoma:
Error: EACCES: permission denied, open '/app/data/file.txt'
Solución:
# Verificar permisos del volumen docker volume inspect mi-volumen # Ajustar ownership docker run --rm -v mi-volumen:/data alpine chown -R 1000:1000 /data
Problema 3: Datos no persisten
Síntoma: Los datos se pierden al recrear el contenedor.
Causas comunes:
- No estás usando el mismo volumen nombrado
- Estás usando un volumen anónimo diferente cada vez
- La ruta del bind mount cambió
Solución:
# Siempre usar volúmenes nombrados docker run -v mi-volumen-nombrado:/data mi-app # NO usar volúmenes anónimos en producción docker run -v /data mi-app # ❌ Evitar
Problema 4: Volumen lleno
Síntoma:
Error: ENOSPC: no space left on device
Solución:
# Ver tamaño del volumen docker system df -v # Limpiar datos antiguos dentro del volumen docker run --rm -v mi-volumen:/data alpine rm -rf /data/old-files # O crear nuevo volumen y migrar datos selectivamente
6.15 Mejores prácticas
✅ Usar volúmenes nombrados en producción (nunca anónimos)
✅ Documentar volúmenes en README del proyecto
✅ Hacer backups regulares de volúmenes críticos
✅ Usar bind mounts solo en desarrollo
✅ Establecer permisos correctos antes de usar volúmenes
✅ Limpiar volúmenes huérfanos periódicamente
✅ Monitorear uso de espacio con docker system df
✅ Usar tmpfs para datos sensibles temporales
❌ NO incluir volúmenes en imágenes (usar .dockerignore)
❌ NO depender de rutas absolutas del host en producción
En resumen, los volúmenes transforman Docker de una herramienta pensada solo para aplicaciones sin estado en una plataforma viable para sistemas reales y persistentes.
Puntos clave:
- Volumes → Producción (gestionados por Docker)
- Bind mounts → Desarrollo (directorios del host)
- tmpfs → Datos temporales sensibles (en memoria)
- Siempre usar volúmenes nombrados en producción
- Hacer backups de datos críticos
- Gestionar permisos correctamente
- Limpiar volúmenes no utilizados regularmente
Entender cómo y cuándo usar cada tipo de almacenamiento es una habilidad clave para cualquier persona que trabaje con Docker en producción.
7. Redes en Docker
Docker proporciona redes para conectar contenedores:
Tipos de redes:
- Bridge: Predeterminado, conecta contenedores en el mismo host.
- Host: Usa directamente la red del host.
- Overlay: Para redes distribuidas.
Ejemplo:
Crear una red personalizada y conectar contenedores:
docker network create mi-red docker run -d --name web --network mi-red nginx docker run -d --name db --network mi-red mysql
8. Docker Compose
Docker Compose facilita la gestión de múltiples contenedores. Define tus servicios en un archivo docker-compose.yml.
Ejemplo de archivo docker-compose.yml:
version: "3.9"
services:
web:
image: nginx
ports:
- "8080:80"
app:
build:
context: .
volumes:
- ".:/app"
ports:
- "3000:3000"
db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: password
Comandos comunes:
- Levantar servicios:
docker-compose up -d - Detener servicios:
docker-compose down
9. Mejores Prácticas con Docker
- Mantén imágenes ligeras:
- Usa imágenes base mínimas como
alpine.
- Usa imágenes base mínimas como
- Evita copiar archivos innecesarios:
- Usa
.dockerignorepara excluir archivos.
- Usa
- Etiqueta tus imágenes:
- Usa versiones para gestionar actualizaciones (
mi-app:1.0).
- Usa versiones para gestionar actualizaciones (
- Usa volúmenes para datos persistentes:
- Nunca almacenes datos críticos dentro del contenedor.
10. Herramientas Complementarias
- Portainer: Interfaz gráfica para gestionar contenedores.
- Docker Swarm: Orquestación para entornos Docker.
- Kubernetes: Gestión avanzada de contenedores en producción.
11. Casos de Uso Comunes para Desarrolladores Web
- Ambientes locales reproducibles:
- Configura tu servidor web, base de datos y backend con Docker.
- Pruebas automáticas:
- Usa contenedores para ejecutar pruebas en entornos aislados.
- Despliegue continuo:
- Integra Docker en pipelines CI/CD.
12. Conclusión
Docker transforma la forma en que los desarrolladores web gestionan y despliegan aplicaciones. Con los fundamentos y herramientas descritas en esta guía, estarás preparado para aprovechar Docker al máximo y llevar tus proyectos al siguiente nivel.
¿Listo para comenzar? ¡Crea tu primer contenedor y experimenta con Docker hoy mismo!