Guía de aprendizaje
Aprende Docker
Docker resuelve un problema viejo: que algo funcione en tu máquina y no en el servidor. Lo hace empaquetando la aplicación con todo lo que necesita para correr, de modo que lo que pruebas en tu portátil es exactamente lo mismo que acaba en producción. Esta guía va de construir esas imágenes y de llevarlas a QA y a producción sin sorpresas.
La ruta de aprendizaje
Diez pasos, de los primeros comandos al despliegue en producción. Los tres primeros están explicados más abajo, en esta misma página; cada uno de los demás tendrá su propia lección.
Imágenes y contenedores
La diferencia que lo explica todo, y los primeros comandos.
Aquí
Capas y caché
Por qué el orden de las instrucciones cambia el tiempo de construcción.
Aquí
La misma imagen en todos los entornos
El principio que hace fiable el despliegue.
Aquí
Configuración por entorno
Variables, archivos .env y por qué los secretos no van en la imagen.
Leer lección →
Etiquetar y publicar en un registro
Etiquetas por commit, latest y por qué importa la distinción.
Leer lección →
Desplegar en QA y en producción
Promover la misma imagen, reversión y comprobaciones de salud.
Leer lección →
Por qué Docker cambia el despliegue
Antes de los contenedores, desplegar consistía en preparar un servidor hasta que la aplicación arrancara: instalar la versión correcta del lenguaje, las bibliotecas del sistema, las variables, los permisos. Ese servidor se volvía único e irrepetible, y reproducirlo para pruebas era un trabajo manual que nunca salía idéntico. De ahí el «en mi máquina funciona»: no era mentira, es que las dos máquinas eran distintas.
Una imagen de Docker invierte el planteamiento. En vez de preparar el servidor para la aplicación, se empaqueta la aplicación con su entorno entero —sistema de archivos, dependencias, configuración— en un archivo inmutable. El servidor solo necesita saber ejecutar contenedores, y lo que ejecuta es bit a bit lo mismo que probaste.
Esa inmutabilidad es lo que hace posible la parte que más interesa aquí: que la imagen que validaste en QA sea literalmente la que se publica en producción, sin reconstruir nada por el camino.
La primera idea: la imagen es la plantilla, el contenedor es la ejecución
Es la distinción que más confusión causa al principio, y sin ella nada de lo demás encaja. Una imagen es un paquete inmutable y de solo lectura. Un contenedor es una ejecución de esa imagen, con una capa escribible propia que desaparece cuando el contenedor se elimina.
De ahí salen dos consecuencias prácticas. La primera es que un contenedor no se actualiza, se reemplaza: para cambiar la aplicación se construye una imagen nueva y se levanta un contenedor nuevo. La segunda es que todo lo que el contenedor escriba en su propio sistema de archivos se pierde al eliminarlo, y por eso las bases de datos y los archivos subidos necesitan volúmenes, que es el paso 7 de la ruta.
La segunda idea: las capas y la caché
Una imagen no es un bloque único: es una pila de capas, una por cada instrucción del Dockerfile. Docker guarda cada capa y la reutiliza si nada ha cambiado, lo que hace que una segunda construcción tarde segundos en vez de minutos.
La regla que se deriva de esto es la que más tiempo ahorra en el día a día: cuando una capa cambia, todas las de debajo se invalidan y hay que rehacerlas. Por eso el orden de las instrucciones importa tanto.
La tercera idea: la misma imagen en QA y en producción
Aquí está el punto que convierte Docker en una herramienta de despliegue y no solo de desarrollo. La tentación natural es construir una imagen para QA y, cuando se aprueba, construir otra para producción. Eso anula la mayor parte del beneficio: entre las dos construcciones pueden cambiar una dependencia, una versión del sistema base o un archivo del repositorio, y lo que se publica ya no es lo que se validó.
La práctica correcta es construir una sola vez, etiquetar la imagen con algo que la identifique sin ambigüedad —normalmente el identificador del commit—, publicarla en un registro, y que cada entorno descargue esa misma imagen. Lo único que cambia entre QA y producción es la configuración que se le pasa al arrancar.
latest no sirve para desplegar. La etiqueta latest no significa nada en Docker: es solo la etiqueta por defecto y apunta a lo último que se subió con ese nombre. Si producción arranca mi-app:latest, nadie puede decir con certeza qué versión está corriendo, ni volver atrás con seguridad. Para desplegar hay que usar una etiqueta inmutable, como el identificador del commit, y dejar latest solo como comodidad de desarrollo.Tu primer contenedor
Lo que sigue se ejecutó con Docker 29.8.1; las salidas están copiadas tal cual. Primero, una imagen mínima que escribe un archivo y lo muestra al arrancar:
FROM alpine:3.22 RUN echo "hola desde la imagen" > /mensaje.txt CMD ["cat", "/mensaje.txt"]
Guardado como Dockerfile, se construye y se ejecuta con dos órdenes:
docker build -t mi-demo:1 . docker run --rm mi-demo:1
salidahola desde la imagen
El -t pone nombre y etiqueta a la imagen, el punto final indica que el contexto de construcción es la carpeta actual, y --rm elimina el contenedor al terminar para no ir dejando restos.
Algo que se pueda visitar
Un contenedor que sirve una página, para ver el mapeo de puertos, que es el otro concepto imprescindible:
FROM alpine:3.22 RUN apk add --no-cache busybox-extras \ && mkdir -p /sitio \ && echo '<h1>hola desde Docker</h1>' > /sitio/index.html EXPOSE 80 CMD ["httpd", "-f", "-p", "80", "-h", "/sitio"]
docker build -t mi-demo:web . docker run -d --name demo-web -p 18080:80 mi-demo:web docker ps curl http://127.0.0.1:18080/
salidademo-web mi-demo:web Up 2 seconds 0.0.0.0:18080->80/tcp <h1>hola desde Docker</h1>
El -p 18080:80 es lo que conecta el puerto 18080 de tu máquina con el 80 de dentro del contenedor. Sin esa conexión el servidor funciona, pero nadie puede llegar a él: un contenedor está aislado de la red del anfitrión salvo que se le abra una puerta explícitamente.
Para limpiar, porque un contenedor arrancado con -d sigue corriendo:
docker rm -f demo-web
El tamaño sí importa
Esas dos imágenes pesan 12,8 MB porque parten de Alpine, una distribución pensada para esto. Para comparar, una imagen construida desde scratch —literalmente la nada— con un único binario estático de 5,3 MB dentro, da este resultado:
docker imagesmi-demo:scratch 6.91MB mi-demo:1 12.8MB
La diferencia parece pequeña en estos números, pero la misma aplicación empaquetada sobre una imagen de sistema completa ronda los cientos de megabytes. El tamaño se paga en cada descarga, en cada despliegue y en cada escalado, y es el tema del paso 5 de la ruta.
Los comandos que se usan a diario
| Qué quieres | Orden |
|---|---|
| Construir una imagen | docker build -t nombre:etiqueta . |
| Ejecutarla y que se borre al salir | docker run --rm nombre:etiqueta |
| Dejarla corriendo en segundo plano | docker run -d --name x nombre |
| Publicar un puerto | -p 8080:80 |
| Pasar una variable de entorno | -e CLAVE=valor |
| Ver lo que está corriendo | docker ps |
| Ver las imágenes locales | docker images |
| Ver los registros de un contenedor | docker logs -f nombre |
| Entrar en un contenedor vivo | docker exec -it nombre sh |
| Pararlo y eliminarlo | docker rm -f nombre |
| Subirla a un registro | docker push registro/nombre:etiqueta |
docker system prune. Es la orden que todo el mundo recomienda para liberar espacio, y borra imágenes, contenedores parados, redes y, con -a, también todo lo que no esté en uso en ese momento. En una máquina de desarrollo con trabajo a medias puede llevarse por delante imágenes que tardaron media hora en construirse. Conviene mirar antes qué va a borrar con docker system df.Dónde seguir mientras tanto
La documentación oficial es buena y está en parte traducida. La guía de inicio de Docker cubre lo básico con ejemplos, y la referencia del Dockerfile es la página que más vas a consultar una vez empieces a escribir los tuyos.
Un consejo que ahorra tiempo desde el principio: no intentes meter tu aplicación entera en un contenedor el primer día. Empieza por contenerizar algo que ya funcione y sea pequeño, haz que construya y arranque, y solo después añade la base de datos, los volúmenes y el resto. Docker tiene muchas piezas y aprenderlas de una en una es bastante más rápido que depurar cinco a la vez.