Antes de escribir tu primer comando, necesitas entender qué problema viniste a resolver. Docker no es una moda ni una herramienta más de la caja del desarrollador: es la respuesta a un dolor concreto y muy antiguo del desarrollo de software, el de que el mismo código se comporte de forma distinta según dónde se ejecute. En esta lección vas a ver cuál es ese dolor, en qué consiste exactamente la contenerización, en qué se diferencia de las máquinas virtuales que quizá ya conoces, y cuáles son los tres conceptos —imagen, contenedor y registro— sobre los que se apoya absolutamente todo lo que verás en el resto del curso. No vas a instalar nada todavía ni a teclear comandos: esta lección construye el mapa mental que hará que los siguientes seis módulos tengan sentido.

Contenido

  1. El problema real: "en mi máquina funciona"
  2. La deriva de entornos y el infierno de dependencias
  3. Qué es la contenerización
  4. Qué es Docker concretamente
  5. Contenedores frente a máquinas virtuales
  6. Los beneficios que obtienes
  7. Casos de uso reales
  8. Los tres conceptos fundamentales: imagen, contenedor y registro
  9. El proyecto que construirás: Aurora Libros

  1. El problema real: "en mi máquina funciona"

Imagina esta escena, porque ocurre todos los días en miles de equipos. Una desarrolladora termina una funcionalidad, la prueba en su portátil, todo va bien y hace push. El servidor de integración continua la compila y falla. Su compañero se descarga la rama y a él sí le funciona. Se despliega en el entorno de preproducción y la aplicación arranca pero devuelve errores raros a las tres horas. La frase que cierra la discusión es siempre la misma: "pues en mi máquina funciona".

El problema no es que nadie mienta. Todos dicen la verdad. El problema es que "mi máquina", "el servidor de CI" y "preproducción" son cuatro entornos distintos que solo se parecen por casualidad:

Elemento Portátil de la desarrolladora Portátil del compañero Servidor de CI Preproducción
Sistema operativo macOS 15 Ubuntu 24.04 Ubuntu 22.04 Debian 12
Node.js 22.11 22.3 20.9 18.20
PostgreSQL 16 (Homebrew) 15 (apt) ninguno 14 (gestionado)
Zona horaria Europe/Madrid Europe/Madrid UTC UTC
Variables de entorno fichero .env local otro .env local secretos del CI variables del proveedor
Librerías del sistema OpenSSL 3.3 OpenSSL 3.0 OpenSSL 3.0 OpenSSL 1.1

Cada una de esas filas es una fuente potencial de fallo. Una aplicación que usa una función de Node añadida en la versión 21 se romperá en el servidor con Node 20. Una consulta SQL que aprovecha una novedad de PostgreSQL 16 fallará contra PostgreSQL 14. Un test que asume la zona horaria local pasará en Madrid y fallará en UTC.

  1. La deriva de entornos y el infierno de dependencias

Hay dos patologías clásicas detrás de todo esto y conviene ponerles nombre porque las vas a oír constantemente.

Deriva de entornos (environment drift). Los entornos empiezan siendo iguales y se van separando con el tiempo. Alguien instala un paquete a mano en el servidor para salir de un apuro y no lo documenta. Otra persona actualiza su Node local porque le apetecía probar algo. Seis meses después, nadie sabe reconstruir el servidor desde cero, y la única documentación fiable es el servidor mismo. Se convierte en lo que se llama un snowflake server: un servidor copo de nieve, único e irrepetible, que da miedo tocar.

Conflicto de dependencias. Tu máquina es una sola y solo puede tener una versión de cada cosa instalada globalmente. Si el proyecto A necesita Python 3.9 y el proyecto B necesita Python 3.12, o si un proyecto necesita PostgreSQL 14 y otro PostgreSQL 16, tienes un problema. Las soluciones tradicionales (gestores de versiones como nvm o pyenv, entornos virtuales) ayudan con el lenguaje, pero no resuelven nada de lo que hay por debajo: librerías del sistema, bases de datos, servidores web, herramientas de línea de comandos.

Y hay un tercer coste, menos visible pero enorme: el onboarding. Cuando entra alguien nuevo al equipo, pasa uno o dos días siguiendo un documento desactualizado para dejar su máquina lista. Instala una base de datos, la configura, carga datos de prueba, instala una caché, ajusta puertos, descubre que le falta una librería del sistema... y aun así algo no funciona. Ese tiempo, multiplicado por cada persona que entra y por cada vez que alguien reinstala su equipo, es dinero.

  1. Qué es la contenerización

La contenerización parte de una idea sencilla: si el problema es el entorno, empaquétalo junto con la aplicación.

Un contenedor es un proceso (o un grupo de procesos) que se ejecuta en tu sistema operativo pero aislado del resto, y que ve un sistema de archivos propio que contiene exactamente lo que la aplicación necesita: su código, su intérprete o runtime, sus librerías, sus ficheros de configuración. Desde dentro del contenedor, la aplicación cree que está sola en una máquina limpia. Desde fuera, es simplemente un proceso más del sistema.

La clave es que ese aislamiento no se consigue emulando hardware ni arrancando otro sistema operativo, sino usando funcionalidades que el propio kernel de Linux ya ofrece para separar procesos entre sí y limitarles los recursos. Por eso un contenedor arranca en milisegundos y consume poquísimo: no hay nada que "encender", solo un proceso que se lanza con una vista restringida del sistema.

En este curso trataremos ese mecanismo con detalle mucho más adelante, en la lección 05-07 (El Runtime por Dentro: Namespaces, Cgroups y Capas). De momento te basta con la intuición: aislamiento a nivel de proceso, no de máquina.

  1. Qué es Docker concretamente

Aquí conviene ser preciso, porque se usan como sinónimos cosas que no lo son:

  • Contenedor es el concepto: un proceso aislado con su propio sistema de archivos.
  • Docker es la plataforma más popular para crear, distribuir y ejecutar contenedores. Incluye un motor que los ejecuta (dockerd), una herramienta de línea de comandos (docker), un formato para describir cómo construir el empaquetado (el Dockerfile) y un servicio público donde compartir esos empaquetados (Docker Hub).
  • Docker, Inc. es la empresa que lo desarrolla.

Docker no inventó los contenedores —Linux llevaba años con las piezas necesarias— pero sí hizo dos cosas decisivas en 2013: los hizo fáciles de usar (un comando en lugar de una configuración esotérica) y, sobre todo, creó un formato estándar para empaquetar y compartir aplicaciones contenerizadas. Ese formato hoy está estandarizado bajo la OCI (Open Container Initiative), lo que significa que una imagen creada con Docker puede ejecutarse en otras herramientas compatibles.

En una frase que puedes memorizar:

Docker es una plataforma que empaqueta una aplicación junto con todo su entorno en una unidad portable y estándar, y la ejecuta de forma aislada y reproducible en cualquier máquina con Docker instalado.

  1. Contenedores frente a máquinas virtuales

Si ya has usado VirtualBox, VMware o máquinas en la nube, esta comparación te colocará el concepto en su sitio. Una máquina virtual también aísla, también empaqueta un entorno completo... pero lo hace a un nivel mucho más bajo y mucho más caro.

Una máquina virtual emula hardware. Sobre ese hardware falso instalas un sistema operativo invitado completo, con su propio kernel, sus servicios de arranque, sus procesos de sistema. Después instalas tu aplicación. El resultado son gigabytes de disco y cientos de megabytes de RAM antes de que tu código haya ejecutado una sola línea.

Un contenedor comparte el kernel del sistema anfitrión. No hay sistema operativo invitado: solo están los ficheros de espacio de usuario que la aplicación necesita.

graph TB
    subgraph VM["Máquinas virtuales"]
        VMHW["Hardware físico"] --> VMHOST["Sistema operativo anfitrión"]
        VMHOST --> HYP["Hipervisor"]
        HYP --> G1["SO invitado 1<br/>(kernel completo)"]
        HYP --> G2["SO invitado 2<br/>(kernel completo)"]
        G1 --> A1["App A + libs"]
        G2 --> A2["App B + libs"]
    end

    subgraph CT["Contenedores"]
        CTHW["Hardware físico"] --> CTHOST["Sistema operativo anfitrión<br/>(un único kernel compartido)"]
        CTHOST --> ENG["Motor de contenedores (Docker)"]
        ENG --> C1["Contenedor 1<br/>App A + libs"]
        ENG --> C2["Contenedor 2<br/>App B + libs"]
        ENG --> C3["Contenedor 3<br/>App C + libs"]
    end

Fíjate en la diferencia estructural del diagrama: en la columna de máquinas virtuales hay un bloque "SO invitado con kernel completo" por cada aplicación; en la de contenedores ese bloque desaparece y todos comparten el kernel del anfitrión. Ahí está el ahorro.

Aspecto Máquina virtual Contenedor
Qué aísla Hardware virtualizado Procesos sobre un kernel compartido
Kernel Uno propio por máquina Compartido con el anfitrión
Tamaño típico 1–20 GB 5 MB – 500 MB
Tiempo de arranque 30 s – varios minutos Milisegundos a 2 s
Densidad por servidor Decenas Cientos o miles
Sobrecarga de CPU/RAM Notable Casi nula
Aislamiento Muy fuerte (frontera de hardware) Fuerte, pero comparte kernel
Sistemas operativos distintos Sí (Windows sobre Linux, etc.) No: el contenedor usa el kernel del anfitrión
Caso típico Aislar inquilinos distintos, ejecutar otro SO Empaquetar y desplegar aplicaciones

Dos matices importantes que no debes olvidar:

  1. No son excluyentes. En la práctica, muchísimos contenedores se ejecutan dentro de máquinas virtuales en la nube. Un proveedor te da una VM y tú metes veinte contenedores dentro.
  2. El aislamiento de un contenedor es bueno, pero no es el de una VM. Al compartir kernel, una vulnerabilidad del kernel afecta a todos los contenedores de la máquina. Por eso existen buenas prácticas de seguridad específicas, que verás en la lección 05-03.

  1. Los beneficios que obtienes

Traduzcamos todo lo anterior a ventajas concretas.

  • Portabilidad. La misma unidad empaquetada se ejecuta igual en tu portátil, en el CI y en producción. Esto es lo que mata el "en mi máquina funciona".
  • Reproducibilidad. El empaquetado se describe en un fichero de texto versionado junto al código. Reconstruir el entorno deja de ser un ritual y pasa a ser un comando.
  • Arranque en segundos. Levantar una base de datos de pruebas o cinco réplicas de un servicio deja de ser una operación pesada.
  • Densidad. Donde cabían 10 máquinas virtuales caben cientos de contenedores, porque no pagas el coste de un sistema operativo por aplicación.
  • Aislamiento. Dos proyectos con versiones incompatibles de todo conviven sin tocarse. Y tu sistema operativo se mantiene limpio: no instalas bases de datos ni runtimes globalmente.
  • Onboarding rápido. Un nuevo miembro del equipo clona el repositorio, ejecuta un comando y tiene toda la plataforma corriendo.
  • Desechabilidad. Si un contenedor se estropea, lo borras y creas otro. Deja de haber servidores "delicados" que nadie se atreve a reiniciar.

  1. Casos de uso reales

Estos son los escenarios donde Docker se ha convertido en el estándar de facto:

Caso de uso Qué resuelve Docker
Desarrollo local Levantar toda la pila (API, base de datos, caché, proxy) con un comando, sin instalar nada en tu sistema
Integración continua (CI) Cada ejecución de tests arranca en un entorno limpio e idéntico; se acabaron los fallos por estado residual del agente
Microservicios Cada servicio se empaqueta, versiona y despliega de forma independiente, con su propio runtime y sus propias dependencias
Despliegue y orquestación La unidad desplegable es la misma imagen probada en CI; plataformas como Kubernetes se apoyan en ese formato
Aplicaciones heredadas Congelar un entorno antiguo (una versión vieja de PHP o Java) que ya no puedes instalar en un servidor moderno
Herramientas puntuales Ejecutar una utilidad sin instalarla: la usas dentro de un contenedor y luego lo borras
Formación y demos Repartir un entorno de prácticas idéntico para todo el mundo

  1. Los tres conceptos fundamentales: imagen, contenedor y registro

Si de esta lección solo te quedas con una cosa, que sea esta. Todo Docker gira alrededor de tres objetos y de la relación entre ellos.

La imagen es la plantilla: un paquete inmutable y de solo lectura que contiene el sistema de archivos de la aplicación (su código, su runtime, sus librerías) y unos metadatos que indican cómo arrancarla. Una imagen no se ejecuta: se usa para crear contenedores.

El contenedor es la instancia en ejecución de una imagen. Es la imagen, más una capa de escritura propia, más un proceso vivo. De una misma imagen puedes crear uno o mil contenedores, y cada uno tendrá su propia vida, sus propios datos temporales y su propio estado.

El registro es el almacén donde viven las imágenes y desde el que se distribuyen. Docker Hub es el registro público por defecto; las empresas suelen tener además registros privados.

La analogía que mejor funciona si vienes de la programación orientada a objetos:

Programación Docker Comentario
Clase Imagen Definición estática, no hace nada por sí sola
Objeto / instancia Contenedor Se crea a partir de la clase, tiene estado propio, puede haber muchos
Repositorio de librerías (npm, Maven) Registro De donde te descargas definiciones hechas por otros

Y el flujo básico que repetirás mil veces durante el curso:

flowchart LR
    D["Tu código +<br/>instrucciones de empaquetado"] -->|construir| I["Imagen"]
    I -->|publicar| R[("Registro<br/>Docker Hub")]
    R -->|descargar| I2["Imagen en otra máquina"]
    I2 -->|ejecutar| C1["Contenedor 1"]
    I2 -->|ejecutar| C2["Contenedor 2"]
    I2 -->|ejecutar| C3["Contenedor 3"]

Léelo así: construyes una imagen a partir de tu código, la publicas en un registro, cualquier máquina la descarga y a partir de ella ejecuta tantos contenedores idénticos como quiera. Esa cadena es, literalmente, la razón de ser de Docker.

En el resto del módulo profundizarás en cada pieza: la arquitectura que hace posible este flujo en la lección 01-03, los comandos para manejarlo en la 01-04, la anatomía de las imágenes en la 01-05 y tu primer contenedor real en la 01-06.

  1. El proyecto que construirás: Aurora Libros

Para que nada de esto se quede en teoría, el curso entero se apoya en un proyecto único que irá creciendo módulo a módulo: Aurora Libros S.L., una librería online ficticia.

Su plataforma tendrá cuatro piezas:

  • aurora-api: la API REST del catálogo, en Node.js 22 con Express.
  • aurora-db: una base de datos PostgreSQL 16 con el catálogo de libros.
  • aurora-cache: un Redis 7 que cachea las consultas más frecuentes.
  • aurora-web: un Nginx que sirve la web estática y hace de proxy inverso hacia la API.

Hoy, en el punto de partida del curso, nada de eso está contenerizado: el equipo de Aurora Libros sufre exactamente los problemas descritos en los apartados 1 y 2. En la última lección de este módulo (01-07) conocerás su código, intentarás arrancarlo sin Docker y verás con tus propios ojos por qué necesitan este curso. A partir de ahí, cada módulo añadirá una pieza hasta llegar a una plataforma completa, segura y desplegada.

Errores Comunes y Consejos

  • Creer que un contenedor es "una VM ligera". Es la analogía más extendida y también la que más confusión genera después. Un contenedor no tiene kernel propio, no arranca un sistema operativo y, en general, ejecuta un solo proceso principal. Cuando ese proceso termina, el contenedor termina. Piensa en "proceso empaquetado", no en "máquina pequeña".
  • Confundir imagen y contenedor. Es el error número uno de los principiantes y arrastra malentendidos durante semanas. Repite la analogía: imagen = clase, contenedor = objeto. Borrar un contenedor no borra su imagen; borrar una imagen no afecta a los contenedores ya creados a partir de ella.
  • Pensar que Docker garantiza que tu aplicación funcione en cualquier sitio. Garantiza que el entorno sea el mismo. No te protege de depender de una arquitectura de CPU concreta (una imagen construida solo para amd64 no arrancará tal cual en un Mac con Apple Silicon), ni de recursos externos que no controlas.
  • Suponer que "contenedor" implica automáticamente "seguro". El aislamiento por defecto es razonable, pero es configurable y se puede debilitar sin querer. La seguridad es un tema propio (lección 05-03).
  • Consejo: aprende el vocabulario antes que los comandos. Los comandos se consultan; los conceptos, no. Si tienes claros imagen, contenedor y registro, el 80 % de los mensajes de error de Docker se vuelven legibles.
  • Consejo: piensa en "desechable". La mentalidad correcta es que cualquier contenedor puede morir en cualquier momento y ser sustituido por otro idéntico. Esa idea guía todas las buenas prácticas que verás más adelante.

Ejercicios

Ejercicio 1: diagnostica el escenario

Un equipo tiene esta situación:

  • La aplicación funciona en el portátil de Marta (Node 22, PostgreSQL 16).
  • Falla en el servidor de CI con un error de sintaxis SQL.
  • El servidor de CI tiene PostgreSQL 14.
  • Nadie recuerda quién instaló PostgreSQL en el CI ni cuándo.

Responde: (a) ¿qué nombre recibe el fenómeno de fondo?, (b) ¿por qué instalar PostgreSQL 16 en el CI no es una solución satisfactoria?, y (c) ¿cómo cambia el planteamiento la contenerización?

Ejercicio 2: contenedor o máquina virtual

Para cada situación, decide si la solución natural es un contenedor, una máquina virtual, o ambos combinados, y justifica en una o dos frases:

  1. Necesitas ejecutar una aplicación de Windows en un servidor Linux.
  2. Quieres que cada uno de los 12 desarrolladores del equipo tenga la misma pila de desarrollo local.
  3. Alquilas capacidad de cómputo a clientes que ejecutarán código arbitrario y no confías en ellos.
  4. Quieres ejecutar 300 instancias de un microservicio pequeño en un único servidor potente.
  5. Necesitas probar tu aplicación contra PostgreSQL 14, 15 y 16 en la misma máquina y a la vez.

Ejercicio 3: traduce la analogía

Sin usar la palabra "Docker", explica a un compañero no técnico la relación entre imagen, contenedor y registro usando una analogía propia (no la de clase/objeto). Después, responde: si borras un contenedor, ¿desaparece la imagen? Y si borras la imagen del registro, ¿se paran los contenedores que ya estaban ejecutándose?

Soluciones

Solución al ejercicio 1

(a) El fenómeno es la deriva de entornos (environment drift): los entornos empezaron pareciéndose y se separaron con el tiempo, agravado por el hecho de que el servidor de CI es un snowflake server cuya configuración nadie sabe reproducir.

(b) Instalar PostgreSQL 16 en el CI arregla este fallo concreto pero no el problema de fondo. Sigue habiendo un servidor configurado a mano, sigue sin haber una descripción versionada del entorno, y mañana el conflicto será con otra pieza (la versión de Node, una librería del sistema, la zona horaria). Además, si otro proyecto del mismo CI necesita PostgreSQL 14, vuelves a tener un conflicto de dependencias irresoluble con una instalación global.

(c) Con contenedores, la versión de PostgreSQL deja de ser una propiedad del servidor y pasa a ser una propiedad del proyecto, declarada en un fichero versionado junto al código. Marta y el CI ejecutan literalmente el mismo empaquetado de PostgreSQL 16, y otro proyecto del mismo CI puede usar la 14 sin interferir. El entorno se vuelve reproducible y desechable en lugar de instalado y permanente.

Solución al ejercicio 2

  1. Máquina virtual. Un contenedor comparte el kernel del anfitrión; un kernel Linux no puede ejecutar binarios de Windows de forma nativa. Necesitas virtualización real.
  2. Contenedores. Es el caso de uso canónico: una definición versionada del entorno que los 12 ejecutan idéntica, sin instalar nada en sus sistemas.
  3. Máquinas virtuales, o contenedores dentro de VMs separadas por cliente. Con código no confiable de terceros conviene la frontera fuerte de la virtualización, porque el kernel compartido de los contenedores es una superficie de ataque común.
  4. Contenedores. La densidad es justamente su gran ventaja: 300 VMs en un servidor serían inviables por la sobrecarga de 300 sistemas operativos completos.
  5. Contenedores. Tres contenedores con tres versiones distintas conviven sin problema, cada uno en su puerto. Con instalaciones nativas sería un ejercicio de paciencia, y con tres VMs pagarías una sobrecarga innecesaria.

Solución al ejercicio 3

Una analogía válida es la de la repostería: la imagen es la receta escrita junto con todos los ingredientes ya medidos y envasados en una caja precintada; el contenedor es la tarta que horneas a partir de esa caja (puedes hornear muchas tartas idénticas con cajas iguales, y cada tarta después vive su propia vida); el registro es la tienda donde se venden esas cajas y de donde cualquiera puede descargarse la que necesite. Otras analogías igualmente correctas: molde/pieza fundida/catálogo de moldes, o plano/edificio/archivo de planos.

Respuestas a las preguntas:

  • Si borras un contenedor, la imagen no desaparece. La imagen es independiente y puede seguir generando contenedores nuevos. (Sí se pierden los datos que ese contenedor hubiera escrito en su capa propia; es un punto importante que se trata en la lección 01-05 y se resuelve en la 03-06.)
  • Si borras la imagen del registro, los contenedores en ejecución siguen funcionando. El registro solo sirve para distribuir: una vez la imagen está descargada en una máquina y hay contenedores creados a partir de ella, el registro ya no interviene. El problema aparecerá el día que otra máquina intente descargarla, o que necesites recrear el contenedor desde cero.

Conclusión

Docker existe porque el software no se ejecuta en el vacío: depende de un entorno, y los entornos derivan. La contenerización ataca el problema empaquetando la aplicación junto con su entorno en una unidad portable, que se ejecuta aislada compartiendo el kernel del anfitrión y, por tanto, sin la sobrecarga de una máquina virtual. De ahí salen sus grandes ventajas: portabilidad, reproducibilidad, arranque inmediato, densidad y aislamiento.

Te llevas tres palabras que vertebrarán todo el curso: la imagen (la plantilla inmutable, la "clase"), el contenedor (la instancia en ejecución, el "objeto") y el registro (el almacén desde el que se distribuyen las imágenes). Y te llevas un proyecto, Aurora Libros, que irás contenerizando pieza a pieza.

Ya sabes qué es Docker y por qué importa. En la siguiente lección, Instalando Docker, pasarás a la práctica: verás la diferencia entre Docker Engine y Docker Desktop, instalarás Docker paso a paso en tu sistema operativo y comprobarás que todo funciona ejecutando tu primerísimo contenedor.

Docker: De Principiante a Avanzado

Módulo 1: Introducción a Docker

Módulo 2: Trabajando con Imágenes Docker

Módulo 3: Contenedores Docker

Módulo 4: Docker Compose

Módulo 5: Conceptos Avanzados de Docker

Módulo 6: Docker en Producción

Módulo 7: Ecosistema y Herramientas de Docker

© Copyright 2026. Todos los derechos reservados