"En mi máquina funciona" es probablemente la frase que más quebraderos de cabeza ha causado en la historia del desarrollo de software. Docker no nació para resolver un problema exótico: nació para resolver exactamente ese, empaquetando una aplicación junto con todo lo que necesita para ejecutarse (dependencias, versión del lenguaje, variables de entorno) de forma que funcione igual en tu portátil, en el servidor de un compañero y en producción.
Este artículo asume que ya sabes programar y que has trabajado con al menos un proyecto real, como el servidor con Node.js y Express que puedes montar siguiendo nuestra guía anterior. Docker no es un lenguaje ni un framework: es una herramienta que envuelve ese tipo de proyectos para que se ejecuten de forma predecible en cualquier sitio.
La diferencia con una máquina virtual
La confusión más habitual al empezar con Docker es pensar que un contenedor es "una máquina virtual ligera". Es una simplificación que ayuda al principio, pero técnicamente son cosas distintas. Una máquina virtual (VM) simula un ordenador completo, incluyendo su propio sistema operativo entero, sobre un hipervisor. Un contenedor, en cambio, comparte el kernel del sistema operativo anfitrión y solo aísla los procesos, el sistema de archivos y la red de la aplicación.
| Aspecto | Máquina virtual | Contenedor Docker |
|---|---|---|
| Sistema operativo | Uno completo por VM | Comparte el kernel del host |
| Peso típico | Gigabytes | Megabytes |
| Arranque | Minutos | Segundos (o menos) |
| Aislamiento | Total (a nivel de hardware virtualizado) | A nivel de procesos del sistema operativo |
| Uso típico | Aislar sistemas operativos distintos | Empaquetar y distribuir aplicaciones |
Esa diferencia es la razón por la que puedes arrancar decenas de contenedores en la misma máquina sin apenas notar la sobrecarga, algo impensable con la misma cantidad de máquinas virtuales.
Imagen vs contenedor: la distinción que hay que interiorizar
Una imagen es una plantilla: un conjunto de capas de solo lectura que definen el sistema de archivos, las dependencias instaladas y el comando que se ejecuta por defecto. Un contenedor es una instancia en ejecución de esa imagen, con su propia capa de escritura encima. Puedes crear varios contenedores a partir de la misma imagen, igual que puedes crear varios objetos a partir de la misma clase en programación orientada a objetos.
# Descarga una imagen del registro público (Docker Hub)
docker pull node:20-alpine
# Crea y arranca un contenedor a partir de esa imagen
docker run -it node:20-alpine node -v
# Lista los contenedores en ejecución
docker ps
# Lista las imágenes descargadas localmente
docker images
Dockeriza tu primera aplicación
Para convertir tu propia aplicación en una imagen necesitas un Dockerfile: un archivo de texto con las instrucciones para construirla. Aquí tienes uno realista para una aplicación Node.js sencilla, como la del servidor Express visto en nuestra guía de Express:
# Dockerfile
FROM node:20-alpine
WORKDIR /app
# Copia primero solo el package.json para aprovechar la caché de capas
COPY package*.json ./
RUN npm install --production
# Ahora copia el resto del código
COPY . .
EXPOSE 3000
CMD ["node", "servidor.js"]
El orden de las instrucciones no es casual: Docker construye la imagen por capas y reutiliza las capas que no han cambiado. Copiar package.json antes que el resto del código significa que, si solo cambias un archivo fuente y no las dependencias, Docker no vuelve a ejecutar npm install desde cero. Con el Dockerfile listo, construyes y arrancas la imagen:
# Construye la imagen y le pone una etiqueta (tag)
docker build -t mi-servidor:1.0 .
# Arranca un contenedor mapeando el puerto 3000 del contenedor
# al puerto 3000 de tu máquina
docker run -p 3000:3000 mi-servidor:1.0
No necesitas dominar Docker Compose ni Kubernetes para empezar. Entender imágenes, contenedores y un Dockerfile básico ya te permite empaquetar y compartir cualquier proyecto propio.
Preguntas frecuentes sobre Docker
Es una plantilla inmutable formada por capas superpuestas de solo lectura: el sistema base, las dependencias instaladas, el código de la aplicación y el comando por defecto que se ejecuta al arrancar. Se define con un Dockerfile y se puede subir a un registro (como Docker Hub) para compartirla.
Un contenedor es una instancia en ejecución de una imagen, con una capa de escritura propia añadida encima de las capas de solo lectura de la imagen. Puedes arrancar, parar y borrar contenedores sin que eso afecte a la imagen original de la que provienen.
Como mínimo: una instrucción FROM que define la imagen base, instrucciones COPY para llevar tu código dentro de la imagen, RUN para instalar dependencias, y CMD para indicar el comando que arranca la aplicación al crear el contenedor. EXPOSE documenta qué puerto usa la app, aunque el mapeo real se hace con -p en docker run.
Una máquina virtual virtualiza hardware y ejecuta un sistema operativo completo propio sobre un hipervisor. Un contenedor comparte el kernel del sistema operativo anfitrión y solo aísla procesos, red y sistema de archivos, lo que lo hace muchísimo más ligero y rápido de arrancar, aunque con un aislamiento algo menos estricto que una VM.
docker build para construir una imagen, docker run para arrancar un contenedor, docker ps para ver los contenedores activos, docker stop y docker rm para pararlos y eliminarlos, y docker logs para revisar la salida de un contenedor cuando algo falla. Con esos seis comandos cubres el 90% del uso diario como desarrollador.
Cuándo merece la pena usar Docker
No todos los proyectos necesitan contenedores desde el primer día. Pero en cuanto trabajas en equipo, despliegas a un servidor distinto de tu máquina, o simplemente quieres asegurarte de que la versión de Node o de una base de datos sea idéntica en todos los entornos, Docker deja de ser un "extra" y pasa a ser la forma más razonable de evitar sorpresas. Es también una herramienta que suele aparecer, aunque sea a nivel conceptual, en entrevistas técnicas para puestos de backend, así que entender estos fundamentos tiene valor más allá del día a día de programar.