Así se automatiza Docker con GitHub Actions

En pocas palabras: Se publica con GitHub Actions usando docker/login-action y docker/build-push-action@v6: cada push a main dispara build y push automático a Docker Hub. El ejemplo de Rakesh Randeria, docker-engineering, genera rakeshranderia/docker-hello (27.5 MB, nginx:alpine) con credenciales guardadas como secrets.

Rakesh Randeria publicó docker-engineering, un repositorio que automatiza la publicación de una imagen Docker en Docker Hub con GitHub Actions. La imagen resultante, rakeshranderia/docker-hello, pesa 27.5 MB, corre sobre nginx:alpine y cada cambio en la rama main dispara un workflow que hace build y push automático con docker/build-push-action@v6.

GitHub Actions para Docker es la combinación de un workflow de CI/CD de GitHub con las acciones oficiales docker/login-action y docker/build-push-action para construir y publicar imágenes de contenedores en un registry como Docker Hub sin intervención manual. El objetivo es que cada push relevante genere una imagen nueva y la suba al registry, mientras las credenciales quedan guardadas como secrets fuera del código fuente.

En 30 segundos

  • Rakesh Randeria publicó docker-engineering, un proyecto que cubre el ciclo completo source → image → registry → publish → runtime, según su artículo en dev.to.
  • La imagen rakeshranderia/docker-hello pesa 27.5 MB y usa nginx:alpine como base, de acuerdo con la ficha en Docker Hub.
  • El workflow usa actions/checkout@v6, docker/login-action@v4 y docker/build-push-action@v6, publicando los tags latest y v1.
  • El trigger combina push a main con path filtering (docker-hello/** y el propio archivo del workflow) más disparo manual vía workflow_dispatch.
  • Las credenciales de Docker Hub viven como secrets (DOCKERHUB_USERNAME y DOCKERHUB_TOKEN), nunca comiteadas en el repo.

Qué cambia cuando el repositorio es la fuente de verdad

Usar GitHub Actions para Docker significa que el repositorio pasa a ser la fuente de verdad del ciclo completo: un cambio en main produce automáticamente la siguiente imagen publicada, sin que nadie tenga que sentarse a correr docker build y docker push desde su máquina.

Randeria lo planteó así desde el arranque: no quería quedarse en el típico “docker build y listo”, sino cubrir el recorrido entero, source → image → registry → automated publish → runtime. La aplicación en sí es mínima, apenas un index.html servido por nginx. El valor no está en lo que hace la app, está en el workflow que la rodea.

Ahí está el punto que suele pasar desapercibido: el registry no es un detalle final del proceso, es parte de la cadena de delivery. Una vez que la imagen llega a Docker Hub, se convierte en un artefacto versionado y portable que cualquiera puede pullear y correr en otro lado, sin depender de la máquina donde se construyó.

El Dockerfile de tres líneas y por qué alcanza

El Dockerfile del proyecto tiene tres líneas: parte de nginx:alpine, copia index.html al directorio web y expone el puerto 80. Nada más. Esa simpleza es intencional: la idea era aislar el workflow de CI/CD de cualquier complejidad de aplicación. Te puede servir nuestra cobertura de desplegar automáticamente a EC2 con Docker Compose.

El código completo es este:

  • FROM nginx:alpine: arranca con una imagen mínima de Nginx.
  • COPY index.html /usr/share/nginx/html/index.html: copia la app estática al webroot.
  • EXPOSE 80: documenta que el contenedor sirve HTTP por ese puerto.

Para correrlo en local, Randeria mapea el puerto 80 del contenedor al 8080 del host con docker run --rm -p 8080:80 rakeshranderia/docker-hello:latest. Esa separación entre puerto de contenedor y puerto de host parece un detalle chico, pero se vuelve clave en el momento en que entran varios servicios en juego.

Antes de tocar GitHub Actions, validó todo el camino a mano: build, push, pull y run. Primero construís la imagen, la subís, la bajás de nuevo y la corrés, confirmás que el registro y el runtime andan bien, y recién ahí automatizás, porque si algo se rompe en el workflow después sabés que no es un problema de la imagen ni de las credenciales, sino del YAML.

El workflow paso a paso: checkout, login y build-push

El workflow se dispara con un push a main que toque archivos relevantes, o manualmente con workflow_dispatch, y corre tres pasos: checkout del repo, login a Docker Hub con secrets, y build + push de la imagen con dos tags.

La secuencia completa, según el YAML publicado en el repositorio, es:

  • actions/checkout@v6: trae el código del repositorio al runner.
  • docker/login-action@v4: autentica contra Docker Hub usando ${ secrets.DOCKERHUB_USERNAME } y ${ secrets.DOCKERHUB_TOKEN }.
  • docker/build-push-action@v6: construye la imagen desde el contexto ./docker-hello y la publica con los tags rakeshranderia/docker-hello:latest y rakeshranderia/docker-hello:v1.

Ojo con un detalle que no es menor: en vez de usar la contraseña de la cuenta de Docker Hub, Randeria generó un token de acceso personal dedicado. Es más prolijo operativamente y, sobre todo, se puede revocar o rotar sin tocar la cuenta entera. Si alguna vez se filtra ese token, lo matás y generás otro, sin cambiar tu password en ningún lado. Esto se conecta con lo que analizamos en comparativa completa de herramientas de CI/CD.

Ejemplo hipotético: cuándo este mismo patrón empieza a no alcanzar

Ejemplo hipotético (no corresponde al proyecto real de Randeria): imaginemos un equipo que arranca copiando esta misma estructura —Dockerfile mínimo, workflow de GitHub Actions, Docker Hub como registry— pero en vez de servir un index.html estático empaqueta una API en Node.js que depende de una base de datos Postgres. El workflow de checkout, login y build-push se puede trasladar prácticamente igual. Lo que cambia es todo lo que el propio artículo de origen señala como “próximo paso lógico”: ahora hace falta un healthcheck real en el Dockerfile, variables de entorno para la conexión a la base en vez de un archivo copiado, y un segundo servicio corriendo en paralelo.

En ese punto, un docker run suelto ya no alcanza para levantar el stack completo a mano, y ahí es donde empieza a justificarse sumar docker-compose (o Kubernetes, si hay varios ambientes y necesidad de autoescalado). Antes de llegar a esa situación, sumar orquestación es complejidad sin beneficio real, que es justamente lo que el proyecto original evita de entrada.

Algunos criterios simples para decidir si este patrón te alcanza tal cual o necesitás más capas:

  • Contenido estático sin estado: el patrón de Randeria (Dockerfile + GitHub Actions + registry) alcanza prácticamente sin modificaciones.
  • Una app que habla con una base de datos u otro servicio: vas a necesitar al menos docker-compose para levantar el stack completo en local antes de pensar en producción.
  • Un solo servicio que necesita reiniciarse solo ante fallas: conviene sumar un HEALTHCHECK en el Dockerfile antes de pensar en orquestación.
  • Múltiples ambientes con necesidad de autoescalado: ahí Kubernetes empieza a pagar su propia complejidad; antes de eso, probablemente no vale la pena.

Costos: minutos de CI y límites de Docker Hub

GitHub Actions no tiene costo para repositorios públicos: los minutos de ejecución en runners de GitHub son gratuitos sin límite declarado para ese tipo de repos, que es justo el caso del proyecto docker-engineering. En repositorios privados, la cuenta tiene minutos incluidos según el plan de GitHub y después se cobra por minuto adicional.

Docker Hub, por su parte, tiene su propia política de límites de pulls y de espacio según el plan de la cuenta. La fuente no detalla cifras de pricing de Docker Hub ni de GitHub Actions para este caso puntual, así que no corresponde inventar un número acá. Lo que sí queda claro en el proyecto es que, al ser público, el costo operativo de correr este workflow es prácticamente nulo.

El filtrado de paths, y por qué importa más de lo que parece

El filtrado de paths hace que el workflow solo se dispare cuando cambia algo relevante: la carpeta docker-hello/** o el propio archivo .github/workflows/docker-publish.yml. Cualquier otro cambio en el repo, como un README o documentación, no activa el build.

En un laboratorio chico como este, el filtrado es casi un ejercicio de aprendizaje. Pero en un repositorio más grande, con varios servicios conviviendo, evita que un typo en la documentación dispare un rebuild completo de una imagen que no cambió en nada. Es la diferencia entre gastar minutos de CI en algo que importa y gastarlos en ruido. Más contexto en elegir entre Jenkins y GitHub Actions.

Limitaciones del setup y los próximos pasos lógicos

El setup actual sirve de referencia de ciclo completo, pero la app que empaqueta es deliberadamente básica: es un archivo HTML estático, sin lógica de negocio, sin health checks y sin comportamiento de API real.

¿Y entonces, para qué sirve si la app no hace nada? Para aprender el patrón sin que la complejidad de la aplicación tape el aprendizaje del workflow. Randeria plantea que el siguiente paso lógico no es sumar más comandos de Docker por sumar, sino aplicar el mismo flujo (build, publish, automatizar con secrets) a una aplicación con runtime real, health checks y comportamiento de API, y recién después sumar orquestación tipo Kubernetes o Docker Compose, cuando la aplicación lo justifique de verdad.

Si en algún momento ese contenedor necesita correr en producción, con tráfico real y necesidad de uptime, ahí entra la elección de dónde alojarlo. Para equipos que buscan un VPS en Argentina con buen soporte local para levantar este tipo de contenedores, donweb.com es una opción a evaluar.

AspectoPublicación manualPublicación con GitHub Actions
Quién ejecuta el buildLa persona, desde su máquinaEl runner de GitHub, automático
TriggerComando manual docker build / pushPush a main con path filtering, o workflow_dispatch
CredencialesLogin manual con usuario/passwordSecrets (DOCKERHUB_USERNAME, DOCKERHUB_TOKEN)
Tags publicadosLos que el desarrollador recuerde taggearlatest y v1 definidos en el workflow
Costo en repos públicosNo aplica (recursos locales)Sin costo declarado en minutos de GitHub Actions
github actions para docker diagrama explicativo

Errores comunes al automatizar la publicación de imágenes Docker

  • Guardar el token de Docker Hub en el código o en un .env commiteado. El workflow de Randeria lo referencia como secret de GitHub, nunca lo escribe en el repo. Si ya lo subiste alguna vez a un commit, revocalo aunque borres el commit después, porque queda en el historial de git.
  • Usar la contraseña de la cuenta en vez de un token dedicado. Si el token se filtra, lo revocás y generás otro sin tocar el acceso general a la cuenta. Si filtrás la contraseña, tenés que cambiarla en todos lados donde la uses.
  • No filtrar paths y disparar builds por cualquier cambio. Sin el bloque paths, un commit de documentación dispara el mismo build que un cambio real en la app, gastando minutos de CI sin necesidad.
  • Saltarse la validación manual antes de automatizar. Probar build, push, pull y run a mano primero separa los problemas de imagen de los problemas de workflow. Si automatizás de una sin validar nada local, cuando algo falle no vas a saber si es el YAML o la imagen.

Preguntas Frecuentes

¿Cómo publico una imagen Docker en Docker Hub automáticamente con GitHub Actions?

Armás un workflow que dispare con push a main, haga login a Docker Hub con docker/login-action usando secrets, y después construya y suba la imagen con docker/build-push-action. Ese es el patrón exacto que usa el proyecto docker-engineering de Randeria, con actions/checkout@v6, docker/login-action@v4 y docker/build-push-action@v6.

¿GitHub Actions es gratis para proyectos con Docker?

En repositorios públicos, los minutos de ejecución en runners de GitHub no tienen costo declarado. En repos privados, cada cuenta tiene minutos incluidos según su plan de GitHub, y después se cobra por minuto adicional usado. Para más detalles técnicos, mirá un caso real de Docker en Agri-Tech.

¿Cómo guardo las credenciales de Docker Hub de forma segura sin exponerlas en el repo?

Las guardás como secrets de GitHub Actions, por ejemplo DOCKERHUB_USERNAME y DOCKERHUB_TOKEN, y el workflow las referencia con la sintaxis ${ secrets.NOMBRE } sin que el valor real aparezca nunca en el código. Lo ideal es usar un token de acceso personal de Docker Hub en vez de la contraseña de la cuenta, porque se puede revocar sin afectar el login general.

¿Qué hace docker/build-push-action en un workflow de GitHub Actions?

docker/build-push-action construye la imagen Docker desde el contexto indicado (en este caso ./docker-hello) y la sube al registry si push está en true, aplicando los tags que definas. En el proyecto de Randeria, con docker/build-push-action@v6, publica los tags latest y v1 en un solo paso.

¿Por qué usar nginx:alpine en vez de una imagen base más pesada?

nginx:alpine mantiene la imagen final chica porque parte de Alpine Linux, una distribución mínima, en vez de una base Linux completa. En este proyecto, la imagen resultante rakeshranderia/docker-hello pesa 27.5 MB, según la ficha publicada en Docker Hub, muy por debajo de lo que pesaría la misma app sobre una imagen base tradicional.

Conclusión

El aporte real del proyecto de Randeria no es la app (un index.html no le gana a nadie), es la referencia completa del ciclo: Dockerfile mínimo, validación manual previa, workflow de GitHub Actions con secrets, y path filtering para no gastar minutos de CI al pedo.

Si estás arrancando con CI/CD para contenedores, el camino que conviene copiar es ese orden: primero build, push, pull y run a mano, después automatizar con GitHub Actions, y recién ahí sumar filtros y optimizaciones. Saltearse el paso manual es la forma más común de terminar debuggeando un workflow sin saber si el problema está en el YAML o en la imagen. Y si en algún momento tu app deja de ser estática y empieza a necesitar base de datos u otro servicio, ese mismo workflow sigue siendo válido: lo que cambia es lo que empaquetás, no cómo lo publicás.

Fuentes

Te puede interesar...