FLASH SALE ⚡

Aprovecha hasta 35% OFF y hasta 12 cuotas sin interés en CURSOS y CARRERAS

|

Hasta el 11/09 ⏰

FLASH SALE ⚡

Aprovecha hasta 35% OFF y hasta 12 cuotas sin interés en CURSOS y CARRERAS

|

Hasta el 11/09 ⏰

Hasta el 11/09 ⏰

FLASH SALE ⚡

Aprovecha hasta 35% OFF y hasta 12 cuotas sin interés en CURSOS y CARRERAS

Cómo desplegar tu primera app con Docker y GitHub Actions: guía paso a paso para developers

Tutoriales gratuitos

Descargartutoriales gratuitos

Tutoriales y videos prácticos gratuitos para incorporar nuevas herramientas de IA y empleabilidad.

Ver los tutoriales

Francisco Rhaiel

AI Growth Engineer

Programación y Desarrollo Web

Cómo desplegar tu primera app con Docker y GitHub Actions: guía paso a paso para developers

Publicado el

Dockerizar una aplicación y desplegarla automáticamente con GitHub Actions es el flujo de trabajo estándar en cualquier equipo de desarrollo moderno. Esta guía lo recorre completo, con el código de cada paso, hasta dejar una app funcionando en la nube que se actualiza sola con cada push.

Es uno de los proyectos que más rinde en un portfolio junior, porque demuestra algo que la mayoría de los candidatos no muestra: que entendés cómo el código llega a producción, no solo cómo se escribe.

Qué vas a construir

Una API sencilla en Python con FastAPI, empaquetada en una imagen de Docker, con un flujo de integración y despliegue continuo en GitHub Actions que corre los tests, construye la imagen y despliega automáticamente en cada push a la rama principal.

El mismo esquema aplica casi sin cambios a Node.js, Go o cualquier otro stack.

Requisitos previos

  • Git y una cuenta de GitHub

  • Docker instalado localmente

  • Python 3.11 o superior

  • Una cuenta gratuita en Railway o Render

Paso 1 — La aplicación

Creá la estructura del proyecto:

  • app/main.py — la aplicación

  • tests/test_main.py — las pruebas

  • requirements.txt — dependencias

  • Dockerfile — la receta de la imagen

  • .github/workflows/deploy.yml — el flujo de CI/CD

En app/main.py:

from fastapi import FastAPI
app = FastAPI(title="Demo API")

@app.get("/health")
def health():
    return {"status": "ok"}

@app.get("/saludo/{nombre}")
def saludo(nombre: str):
    return {"mensaje": f"Hola, {nombre}"}

En requirements.txt:

fastapi==0.115.0
uvicorn[standard]==0.32.0
pytest==8.3.3
httpx==0.27.2

Fijar versiones exactas no es un detalle: es lo que hace que la imagen que construís hoy sea igual a la que construís en tres meses.

En tests/test_main.py:

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_health():
    r = client.get("/health")
    assert r.status_code == 200
    assert r.json() == {"status": "ok"}

El endpoint /health parece trivial pero es fundamental: las plataformas de despliegue lo usan para saber si tu app arrancó bien.

Paso 2 — El Dockerfile

Un Dockerfile mal escrito produce imágenes de 1 GB que tardan cinco minutos en construirse. Este usa construcción en múltiples etapas y aprovecha la caché de capas:

FROM python:3.11-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app ./app

RUN useradd -m appuser
USER appuser

EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Tres decisiones que importan:

  • Copiar requirements.txt antes que el código. Docker cachea por capas: si el código cambia pero las dependencias no, no reinstala nada. Esto reduce el tiempo de construcción de minutos a segundos.

  • Usar python:3.11-slim en lugar de la imagen completa. Diferencia de cientos de megabytes.

  • No correr como root. Es una de las recomendaciones básicas de la documentación oficial de Docker y de las más ignoradas.

Agregá también un .dockerignore con .git, __pycache__, .venv, tests y .env. Sin él, estás copiando basura y, en el peor caso, credenciales a la imagen.

Probalo localmente

docker build -t demo-api:local .
docker run -p 8000:8000 demo-api:local

Abrí http://localhost:8000/health. Si responde, la imagen está bien. Nunca pases al paso siguiente sin que esto funcione: depurar un Dockerfile desde los logs de CI es mucho más lento.

Paso 3 — El workflow de GitHub Actions

Un workflow es un archivo YAML que le dice a GitHub qué ejecutar y cuándo. En .github/workflows/deploy.yml:

name: CI/CD

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: pip
      - run: pip install -r requirements.txt
      - run: pytest -q

  build-and-deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t demo-api:${{ github.sha }} .
      - name: Deploy
        env:
          DEPLOY_HOOK: ${{ secrets.DEPLOY_HOOK }}
        run: curl -fsSL -X POST "$DEPLOY_HOOK"

Qué está pasando acá

  • on define los disparadores: cada push a main y cada pull request.

  • Dos jobs separados. test corre siempre; build-and-deploy solo si los tests pasaron (needs: test) y solo en la rama principal. Esa condición es lo que evita desplegar código roto.

  • github.sha como etiqueta. Cada imagen queda identificada con el commit exacto que la generó, lo que hace trivial saber qué versión está corriendo.

  • secrets.DEPLOY_HOOK. La URL de despliegue nunca va en el código. Se carga en Settings → Secrets and variables → Actions del repositorio.

La documentación oficial de GitHub Actions cubre el resto de la sintaxis: matrices de versiones, entornos con aprobación manual y reutilización de workflows.

Paso 4 — Desplegar en Railway o Render

Railway

  • Creá un proyecto y conectá tu repositorio de GitHub.

  • Railway detecta el Dockerfile automáticamente y lo usa para construir.

  • Configurá el puerto en las variables de entorno y ajustá el CMD para leer $PORT si la plataforma lo asigna dinámicamente.

  • Copiá la URL del deploy hook y guardala como secreto en GitHub.

Render

  • Creá un Web Service apuntando al repositorio, con entorno "Docker".

  • Definí el health check path en /health.

  • Render genera una Deploy Hook URL en la configuración del servicio; ese es el valor del secreto.

Ambas tienen plan gratuito suficiente para un proyecto de portfolio. La contrapartida es que suelen suspender el servicio tras un período de inactividad, así que la primera petición después de un rato puede tardar unos segundos.

Paso 5 — Verificar que funciona

Hacé un cambio mínimo, por ejemplo modificá el mensaje del endpoint /saludo, y subilo:

git add . && git commit -m "cambio de prueba" && git push origin main

En la pestaña Actions del repositorio deberías ver el workflow corriendo: primero los tests, después la construcción y el despliegue. En uno o dos minutos, la URL pública debería devolver el nuevo mensaje.

Si algo falla, el orden de revisión es: logs del job que falló → reproducir el mismo comando localmente → verificar que los secretos estén cargados con el nombre exacto.

Errores frecuentes y cómo resolverlos

Síntoma

Causa habitual

Solución

La imagen pesa más de 1 GB

Imagen base completa, falta .dockerignore

Usar -slim y excluir archivos innecesarios

El build tarda varios minutos siempre

Se copia el código antes de las dependencias

Copiar requirements.txt primero

La app no responde en la nube

Escucha en 127.0.0.1 en vez de 0.0.0.0

Usar --host 0.0.0.0

El deploy corre aunque fallen los tests

Falta needs: entre jobs

Agregar la dependencia entre jobs

Credenciales expuestas en logs

Variables impresas o hardcodeadas

Usar secrets y nunca hacer echo de ellos

Cómo presentar esto en tu portfolio

El repositorio vale más si el README explica las decisiones, no solo los comandos. Incluí:

  • Un diagrama simple del flujo: push → tests → build → deploy

  • Por qué elegiste construcción en capas y qué tiempo de build ahorraste

  • Qué pasa cuando un test falla (con captura del workflow en rojo)

  • La URL pública funcionando

Ese contexto es lo que convierte un repositorio más en un caso que podés defender en una entrevista. Si querés profundizar en el ecosistema de contenedores más allá de este proyecto, este artículo sobre qué son Docker y Kubernetes y cómo aprenderlos siendo desarrollador junior ordena el camino siguiente.

Cursos recomendados de Coderhouse

Para consolidar lo que vimos acá y avanzar hacia un perfil de infraestructura:

  • Curso de DevOps & Cloud — el recorrido directo de este artículo: contenedores, CI/CD, infraestructura como código y monitoreo.

  • Curso de Cloud Computing AWS — el siguiente paso natural cuando querés salir de plataformas gestionadas y manejar la infraestructura vos.

  • Curso de Python — si el stack del ejemplo te resultó nuevo, acá está la base del lenguaje.

  • Carrera de Desarrollo Backend — el recorrido completo de APIs, bases de datos y arquitectura, con despliegue incluido.

Preguntas frecuentes

¿Necesito saber Docker para conseguir trabajo como desarrollador junior?

No es requisito excluyente en la mayoría de las búsquedas junior, pero aparece cada vez más y es uno de los diferenciales más baratos de conseguir. Entender cómo empaquetar una aplicación y por qué eso resuelve el problema de "en mi máquina funciona" te separa de la mayoría de los candidatos de entrada.

¿Cuál es la diferencia entre integración continua y despliegue continuo?

La integración continua (CI) es la parte que ejecuta automáticamente los tests y las verificaciones cada vez que alguien sube código: su objetivo es detectar problemas temprano. El despliegue continuo (CD) es la parte que lleva ese código a producción sin intervención manual. El workflow de esta guía hace las dos cosas: el job test es CI y el job build-and-deploy es CD.

¿Railway o Render: cuál conviene para un proyecto de portfolio?

Ambas funcionan bien y tienen plan gratuito. Railway suele ser más rápida de configurar y tiene mejor experiencia con bases de datos asociadas. Render tiene health checks más explícitos y documentación más clara sobre Docker. Para un proyecto de portfolio, elegí la que configures más rápido: lo que se evalúa es el flujo, no la plataforma.

¿Qué pasa si mi aplicación necesita una base de datos?

El esquema no cambia demasiado: agregás la base como servicio en la plataforma de despliegue, pasás la cadena de conexión como variable de entorno (nunca en el código), y en el workflow de CI usás un servicio de base de datos temporal para correr los tests. GitHub Actions permite levantar contenedores auxiliares dentro del job para exactamente eso.

¿Conviene publicar la imagen en un registry?

Para un proyecto de portfolio no es imprescindible, pero suma. GitHub Container Registry está integrado con Actions y se configura en pocas líneas. La ventaja real es de trazabilidad: podés desplegar exactamente la misma imagen que testeaste, en lugar de reconstruirla en el servidor de destino, que es la práctica correcta en entornos de producción.

Sobre el autor

Francisco Rhaiel

Soy Francisco Rhaiel, AI Growth Engineer en Coderhouse. Mi día a día consiste en automatizar y optimizar procesos aplicando lo último en inteligencia artificial, incluyendo Agentic AI y Gen AI. Soy graduado de la Universidad Torcuato Di Tella (UTDT) en Tecnología Digital, y mi recorrido me llevó a especializarme en la intersección entre tecnología, datos y negocio. Me mueve aprender, construir y aplicar tecnologías innovadoras para resolver problemas reales. Para profundizar en mi trayectoria, te espero en mi perfil de LinkedIn.

Imagen promocionando quiz gratis de Coderhouse para encontrar tu formación.
Imagen promocionando quiz gratis de Coderhouse para encontrar tu formación.
Imagen promocionando quiz gratis de Coderhouse para encontrar tu formación.
Imagen promocionando quiz gratis de Coderhouse para encontrar tu formación.
Argentina

© 2026 Coderhouse. Todos los derechos reservados.

Argentina

© 2026 Coderhouse. Todos los derechos reservados.

Argentina

© 2026 Coderhouse. Todos los derechos reservados.

Argentina

© 2026 Coderhouse. Todos los derechos reservados.