Production AI Infrastructure

Qué es `ghcr.io` y cómo usarlo con Helm y Argo CD para una app de FastAPI

Una guía práctica, respaldada por fuentes, sobre GitHub Container Registry, charts OCI de Helm, Argo CD y un flujo claro de local/dev/prod para un servicio sencillo de FastAPI.

30 min de lectura

ghcr.io aparece en muchos ejemplos modernos de despliegue, pero muchas guías pasan por alto la pregunta más importante: ¿qué hace realmente dentro del flujo?

Si estás desarrollando una app sencilla de FastAPI, la confusión suele verse así:

  • el desarrollo local usa Docker Compose
  • CI construye una imagen y la publica en algún lugar
  • aparece Helm para Kubernetes
  • aparece Argo CD para GitOps
  • y de pronto nadie tiene del todo claro qué sistema se encarga de cada cosa

Esta guía busca aclararlo tanto para ingenieros como para PMs. Los ejemplos son sencillos a propósito, pero el modelo de despliegue está pensado para producción.

La investigación que sigue se contrastó con documentación primaria el April 8, 2026.

TL;DR

  • ghcr.io es el nombre de host de GitHub Container Registry. Almacena artefactos compatibles con OCI, lo que significa que puede guardar tus imágenes de contenedor y charts de Helm empaquetados como OCI.
  • Para un servicio de FastAPI, un modelo mental claro es: local con Docker Compose, dev al hacer merge a main y prod con un tag de Git.
  • GHCR es un registry, no un sistema de despliegue. Publicar una imagen o un chart en GHCR no despliega nada por sí solo.
  • Helm debe definir el contrato de ejecución para Kubernetes. Argo CD debe reconciliar lo que está en Git con lo que está en el clúster.
  • La configuración más confiable es mantener las imágenes y los charts en GHCR, y la intención de despliegue de cada entorno en Git para que Argo CD pueda sincronizarla.
  • Para dev, automatiza los merges a main. Para prod, automatiza los tags de versión semántica como v1.4.0.
  • Usa referencias inmutables de imagen para los despliegues reales, aunque también publiques tags móviles más fáciles de identificar como main.

Qué aprenderás aquí

  • qué es realmente ghcr.io
  • cómo se relaciona GHCR con las imágenes de Docker y los charts de Helm
  • cómo estructurar un repo sencillo de FastAPI para local, dev y prod
  • cómo deben repartirse las responsabilidades Docker Compose, Helm y Argo CD
  • cómo automatizar los despliegues a dev al hacer merge a main
  • cómo automatizar los despliegues a prod con tags de Git
  • qué atajos mejoran la experiencia de desarrollo y cuáles generan problemas en el futuro

El modelo de entrega que buscamos

Esta es la visión completa antes de entrar en las herramientas:

Local laptop
  |
  | docker compose up --watch
  v
FastAPI app runs locally

Merge to main
  |
  v
GitHub Actions
  |- build image
  |- push image to ghcr.io
  |- package Helm chart
  |- push chart to ghcr.io
  `- update GitOps repo for dev
          |
          v
      Argo CD notices Git change
          |
          v
      dev namespace syncs

Create tag v1.2.0
  |
  v
GitHub Actions
  |- build release image
  |- push image to ghcr.io
  |- package release Helm chart
  |- push chart to ghcr.io
  `- update GitOps repo for prod
          |
          v
      Argo CD notices Git change
          |
          v
      prod namespace syncs

Ese flujo parte de una decisión arquitectónica importante:

GHCR almacena artefactos. Git almacena la intención de despliegue. Argo CD aplica esa intención.

Esa separación mantiene el sistema comprensible a medida que más personas se suman al proyecto.

Qué es realmente ghcr.io

ghcr.io es el endpoint del registry de GitHub Container Registry, que forma parte de GitHub Packages.

En términos prácticos, te ofrece un lugar para publicar artefactos como estos:

ArtefactoEjemplo
Imagen de Dockerghcr.io/acme/hello-fastapi:sha-abc1234
Tag de imagen fácil de identificarghcr.io/acme/hello-fastapi:main
Chart OCI de Helmoci://ghcr.io/acme/charts/hello-fastapi --version 1.2.0

Lo que hace que GHCR sea especialmente conveniente para equipos que usan GitHub:

  • GitHub documenta el soporte para imágenes Docker y OCI en el container registry.
  • El registry admite permisos granulares.
  • Las imágenes públicas se pueden descargar de forma anónima.
  • Dentro de GitHub Actions, el workflow puede publicar paquetes asociados al mismo repositorio mediante GITHUB_TOKEN.

Así que, si tu código ya está en GitHub, GHCR elimina mucha fricción:

  • no necesitas un segundo proveedor solo para alojar imágenes
  • la autenticación en Actions es sencilla
  • los charts y las imágenes pueden alojarse cerca del repo que los generó

El matiz clave es este:

GHCR no es Helm ni Argo CD.

  • GHCR responde: «¿Dónde se almacenan los artefactos generados?».
  • Helm responde: «¿Cómo debe ejecutarse esta app en Kubernetes?».
  • Argo CD responde: «¿Coincide el clúster con lo que Git indica que debe ejecutarse?».

Una vez que separas esas responsabilidades, la arquitectura es mucho más fácil de entender.

La estructura de repos que recomiendo

Para un equipo pequeño, me gusta esta separación:

hello-fastapi/
├── app/
│   └── main.py
├── charts/
│   └── hello-fastapi/
├── compose.yaml
├── Dockerfile
└── .github/
    └── workflows/

hello-fastapi-env/
├── apps/
│   ├── dev.yaml
│   └── prod.yaml
└── envs/
    ├── dev/values.yaml
    └── prod/values.yaml

¿Por qué dos repos?

  • El repo de la aplicación se encarga del código, la construcción de la imagen y el empaquetado del chart.
  • El repo de entornos se encarga de la intención de despliegue para dev y prod.
  • Argo CD observa el repo de entornos, lo que mantiene GitOps sencillo.

Por supuesto, puedes mantener todo en un solo repo cuando el equipo es pequeño. Pero una vez que las reglas de promoción entre dev y prod empiezan a ser importantes, un repo de entornos separado suele valer la pena rápidamente.

Desarrollo local: mantenlo simple y rápido con Docker Compose

Para el trabajo local, el objetivo no es simular todo el clúster. El objetivo es poder editar, ejecutar y validar la app rápidamente.

Una app mínima de FastAPI:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    return {"message": "hello from FastAPI"}


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

Un Dockerfile sencillo:

FROM python:3.12-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

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

COPY app ./app

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

Y un compose.yaml local cómodo para el desarrollo:

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      APP_ENV: local
    healthcheck:
      test:
        [
          "CMD",
          "python",
          "-c",
          "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz')",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
    develop:
      watch:
        - action: sync
          path: ./app
          target: /app/app
          initial_sync: true
        - action: rebuild
          path: ./requirements.txt

  mailhog:
    image: mailhog/mailhog:latest
    profiles: ["debug"]
    ports:
      - "8025:8025"

Ejecuta el flujo habitual:

docker compose up --watch

O activa herramientas opcionales solo cuando las necesites:

docker compose --profile debug up --watch

Ese patrón encaja bien con la documentación actual de Docker:

  • watch mejora el ciclo local automatizado para servicios construidos desde el código fuente
  • profiles permite mantener herramientas opcionales en el mismo archivo de Compose
  • las verificaciones de salud importan porque Compose no supone que «en ejecución» signifique «listo»

Para desarrollo local, esto es más que suficiente. No necesitas Helm ni Argo CD en tu laptop solo para editar un endpoint de la API.

Helm: empaqueta el contrato de Kubernetes, no solo un poco de YAML

Una vez que la app sale de la laptop, Helm resulta útil porque define el contrato de ejecución para Kubernetes.

Estructura típica de un chart:

charts/hello-fastapi/
├── Chart.yaml
├── values.yaml
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    └── serviceaccount.yaml

Un Chart.yaml mínimo:

apiVersion: v2
name: hello-fastapi
description: A Helm chart for a simple FastAPI app
type: application
version: 1.2.0
appVersion: 1.2.0

Un values.yaml mínimo:

image:
  repository: ghcr.io/acme/hello-fastapi
  tag: "1.2.0"
  pullPolicy: IfNotPresent

service:
  port: 8000

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi

env:
  APP_ENV: prod

Y un fragmento útil de Deployment:

containers:
  - name: api
    image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
    imagePullPolicy: {{ .Values.image.pullPolicy }}
    ports:
      - name: http
        containerPort: 8000
    env:
      - name: APP_ENV
        value: {{ .Values.env.APP_ENV | quote }}
    readinessProbe:
      httpGet:
        path: /healthz
        port: http
    livenessProbe:
      httpGet:
        path: /healthz
        port: http

Aquí es donde Helm demuestra su valor:

  • la referencia de la imagen está centralizada
  • los valores de dev y prod se mantienen explícitos
  • el chart se convierte en el contrato de despliegue reutilizable

Usar GHCR para charts OCI de Helm

Helm admite registries basados en OCI, lo que significa que tu chart puede estar en GHCR junto a la imagen de la app.

El flujo básico es:

helm registry login ghcr.io -u YOUR_GITHUB_USERNAME
helm package charts/hello-fastapi --destination .dist
helm push .dist/hello-fastapi-1.2.0.tgz oci://ghcr.io/acme/charts

Aquí importan algunos detalles:

  • el destino del push debe comenzar con oci://
  • helm push infiere el nombre del chart a partir de Chart.yaml
  • el tag del chart debe coincidir con la versión semántica del chart
  • para los comandos de descarga e instalación, el nombre del chart forma parte de la referencia

Más adelante, un pull se ve así:

helm pull oci://ghcr.io/acme/charts/hello-fastapi --version 1.2.0

Y una renderización local se ve así:

helm template hello-fastapi oci://ghcr.io/acme/charts/hello-fastapi --version 1.2.0

Esto te da una separación clara:

  • artefacto de imagen en GHCR
  • artefacto de chart en GHCR
  • valores de entorno en Git

Argo CD: Git debe expresar la intención de despliegue

Esta es la parte que a menudo se simplifica demasiado.

A veces se describe el sistema así:

push image to registry -> Argo CD magically deploys it

Eso está incompleto.

El modelo de sincronización automática de Argo CD se centra en Git. Sincroniza automáticamente cuando detecta diferencias entre los manifiestos deseados en Git y el estado actual del clúster. En otras palabras, el registry almacena artefactos, pero Git debe seguir indicando qué revisión debe usar cada entorno.

Por eso recomiendo:

  • GHCR para imágenes y charts
  • Git para la intención de despliegue de dev/prod
  • sincronización automática de Argo CD desde Git

Configuración de Argo CD para un chart de Helm almacenado en GHCR

Para un repositorio privado de charts alojado en GHCR, Argo CD necesita credenciales de repositorio con OCI habilitado:

apiVersion: v1
kind: Secret
metadata:
  name: ghcr-helm
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  url: ghcr.io/acme/charts
  type: helm
  enableOCI: "true"
  username: gh-bot
  password: ${GHCR_TOKEN}

Luego, la Application puede apuntar al chart OCI de Helm y obtener los valores de un repo de Git:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: hello-fastapi-dev
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: ghcr.io/acme/charts
      chart: hello-fastapi
      targetRevision: 0.1.0-dev.128
      helm:
        valueFiles:
          - $values/envs/dev/values.yaml
    - repoURL: https://github.com/acme/hello-fastapi-env.git
      targetRevision: main
      ref: values
  destination:
    server: https://kubernetes.default.svc
    namespace: hello-fastapi-dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Es fácil pasar por alto dos matices:

  • Cuando Argo CD usa el campo chart de Helm para charts OCI, repoURL no incluye el prefijo oci://.
  • Argo CD puede obtener los valores de Helm de un repositorio separado, lo que es extremadamente útil para los repos de entornos de GitOps.

Esto te da un flujo de promoción muy claro:

  • el chart está en GHCR
  • la imagen está en GHCR
  • los valores de entorno están en Git
  • Argo CD sincroniza después de los cambios en Git

La estrategia de entornos que recomiendo

Esta es la separación práctica:

EntornoDisparadorQué se construyeQué despliega Argo CD
localdocker compose up --watchsolo la imagen localnada en el clúster
devmerge a mainimagen + paquete de chart para devversión exacta del chart de dev y tag exacto de la imagen en Git
prodpush de un tag como v1.2.0imagen de release + paquete de chart de releaseversión exacta del release en Git

La palabra importante es exacta.

Para identificar cada despliegue:

  • usa tags de imagen inmutables como sha-<git-sha> o versiones semánticas
  • usa versiones semánticas para los charts
  • haz que Git apunte cada entorno a una versión precisa

Puedes seguir publicando tags prácticos como main, pero no los uses como fuente de verdad para producción.

Automatización de dev: merge a main

Para desarrollo, el flujo más fluido es:

  1. Se hace merge de un PR a main.
  2. GitHub Actions construye la imagen.
  3. El workflow publica la imagen en GHCR con:
    • un tag inmutable sha-<commit>
    • un tag móvil main opcional, fácil de identificar
  4. El workflow empaqueta una versión del chart para dev como 0.1.0-dev.128.
  5. El workflow publica el chart en GHCR.
  6. El workflow actualiza el repo de GitOps para dev.
  7. Argo CD sincroniza dev automáticamente.

Un esquema compacto del workflow:

name: build-dev

on:
  push:
    branches: [main]

permissions:
  contents: read
  packages: write

jobs:
  publish:
    runs-on: ubuntu-latest
    env:
      REGISTRY: ghcr.io
      IMAGE_NAME: ${{ github.repository }}
      CHART_REPO: oci://ghcr.io/${{ github.repository_owner }}/charts
    steps:
      - uses: actions/checkout@v5

      - name: Compute versions
        id: vars
        run: |
          echo "image_tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
          echo "chart_version=0.1.0-dev.${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT"

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.image_tag }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Package and push chart
        run: |
          CHART_VERSION="${{ steps.vars.outputs.chart_version }}"
          mkdir -p .dist
          sed \
            -e "s/^version: .*/version: ${CHART_VERSION}/" \
            -e "s/^appVersion: .*/appVersion: ${GITHUB_SHA}/" \
            charts/hello-fastapi/Chart.yaml > /tmp/Chart.yaml
          cp /tmp/Chart.yaml charts/hello-fastapi/Chart.yaml
          helm package charts/hello-fastapi --destination .dist
          helm push .dist/hello-fastapi-${CHART_VERSION}.tgz $CHART_REPO

      - name: Update GitOps repo for dev
        run: ./scripts/promote-env.sh dev \
          "${{ steps.vars.outputs.image_tag }}" \
          "${{ steps.vars.outputs.chart_version }}"

En un repo real, fijaría las actions a SHAs de commits, aunque el ejemplo anterior usa tags de versión mayor para facilitar la lectura.

Automatización de prod: crear un tag de Git

Para producción, el evento no es un merge. Es una señal de release.

Recomiendo usar tags de versión semántica:

git tag v1.2.0
git push origin v1.2.0

Luego, el workflow debe:

  1. obtener 1.2.0 a partir de v1.2.0
  2. construir y publicar ghcr.io/acme/hello-fastapi:1.2.0
  3. publicar también sha-<commit> para trazabilidad
  4. empaquetar la versión 1.2.0 del chart
  5. publicar el chart en GHCR
  6. actualizar el repo de GitOps para prod
  7. dejar que Argo CD sincronice prod

Ejemplo de esquema:

name: release-prod

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: read
  packages: write

jobs:
  release:
    runs-on: ubuntu-latest
    env:
      REGISTRY: ghcr.io
      IMAGE_NAME: ${{ github.repository }}
      CHART_REPO: oci://ghcr.io/${{ github.repository_owner }}/charts
    steps:
      - uses: actions/checkout@v5

      - name: Compute release version
        id: vars
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push release image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.version }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Package and push release chart
        run: |
          VERSION="${{ steps.vars.outputs.version }}"
          mkdir -p .dist
          sed \
            -e "s/^version: .*/version: ${VERSION}/" \
            -e "s/^appVersion: .*/appVersion: ${VERSION}/" \
            charts/hello-fastapi/Chart.yaml > /tmp/Chart.yaml
          cp /tmp/Chart.yaml charts/hello-fastapi/Chart.yaml
          helm package charts/hello-fastapi --destination .dist
          helm push .dist/hello-fastapi-${VERSION}.tgz $CHART_REPO

      - name: Update GitOps repo for prod
        run: ./scripts/promote-env.sh prod \
          "${{ steps.vars.outputs.version }}" \
          "${{ steps.vars.outputs.version }}"

Si tu equipo quiere una aprobación obligatoria para producción, haz que ese último paso abra un PR en el repo de entornos en lugar de hacer commit directamente. Para muchos equipos, esa es la mejor opción por defecto.

La idea que mantiene esto bajo control

Hay dos formas claras de automatizar las actualizaciones después de que los artefactos llegan a GHCR:

Opción A: CI actualiza el repo de GitOps

Este es el modelo más sencillo de explicar y mantener.

  • CI publica los artefactos
  • CI hace commit de la versión deseada en Git
  • Argo CD sincroniza desde Git

Esta suele ser mi recomendación por defecto para equipos pequeños y medianos.

Opción B: Argo CD Image Updater escribe los cambios en Git

Esto es útil si quieres que Argo CD observe el registry de manera más directa sin dejar de usar Git como fuente de verdad persistente.

La documentación actual de Image Updater admite:

  • la estrategia digest para tags mutables como main
  • la estrategia semver para tags de release
  • escritura en Git para que las actualizaciones se guarden allí en lugar de modificar solo la Application activa

Si eliges esta opción, usa el método Git write-back en lugar del método de escritura directa argocd para entornos de larga duración. Es más compatible con GitOps y más persistente.

¿Dónde deberías desplegar esta app?

Aquí es donde muchos equipos mezclan accidentalmente dos preguntas diferentes:

  1. «¿Cuál es el lugar más fácil y económico para ejecutar una app sencilla de FastAPI?».
  2. «¿Cuál es el mejor lugar para conservar el flujo exacto de GHCR + Helm + Argo CD de este artículo?».

Esas preguntas no siempre conducen a la misma plataforma.

Si tu objetivo principal es el hosting sencillo, las plataformas como Render, Railway o Cloud Run suelen ser más fáciles de usar.

Si tu objetivo principal es GitOps en Kubernetes con Helm y Argo CD, las opciones de Kubernetes administrado como EKS o GKE encajan mejor.

Comparación rápida de costo y complejidad

Estas cifras no representan los costos totales de producción. Son precios de entrada oficiales o ejemplos de precios oficiales revisados el April 8, 2026. El gasto mensual real también depende de la elección de base de datos, el almacenamiento, el tráfico, los logs, los balanceadores de carga, la región y el plan del equipo.

OpciónReferencia de costo mensual según la documentación oficialComplejidadMejor opción paraCompatibilidad con GHCR + Helm + Argo CD
Cloudflare WorkersEl plan de pago tiene un mínimo de $5/mes, incluye 10 millones de solicitudes y 30 millones de ms de CPU, sin cargos adicionales por tráfico de salida de WorkersBaja a mediaAPIs orientadas al edge y apps ligeras de FastAPI compatibles con el modelo de WorkersBaja
Cloudflare ContainersIncluido en Workers Paid con 25 GiB-horas, 375 vCPU-minutos y 200 GB-horas, seguido de facturación por uso; el tráfico de salida de contenedores se cobra por separado según la regiónMediaCargas de trabajo en contenedores en Cloudflare cuando quieres un entorno de ejecución más parecido al de un contenedor que WorkersBaja a media
Render Web ServiceEl workspace Hobby cuesta 0/usuario/mesmaˊscoˊmputo∗∗;losservicioswebcomienzanen∗∗0/usuario/mes más cómputo**; los servicios web comienzan en **0 y el primer nivel de pago para servicios web cuesta $7/mesBajaLa experiencia de desarrollo más rápida para un equipo pequeño que entrega una API en DockerBaja
RailwayFree cuesta 0/mes∗∗;Hobbycuesta∗∗0/mes**; Hobby cuesta **5/mes e incluye 5∗∗deuso;Procuesta∗∗5** de uso; Pro cuesta **20/mes e incluye $20 de uso. Los registries privados como GHCR requieren ProBajaEquipos pequeños que buscan una buena experiencia de desarrollo, facturación por uso, flexibilidad de Docker y despliegues opcionales de imágenes de GHCRBaja a media
AWS App RunnerLos ejemplos oficiales muestran 4.80/mes∗∗paraunaappdedev/testpausaday∗∗4.80/mes** para una app de dev/test pausada y **25.50/mes para una API ligera sensible a la latenciaMediaEquipos que ya usan AWS y quieren contenedores administrados sin operar KubernetesBaja
AWS EKS0.10/cluˊster−hora∗∗parasoporteestaˊndar,aproximadamente∗∗0.10/clúster-hora** para soporte estándar, aproximadamente **73/mes antes de los nodos de trabajo y otros recursos de AWSAltaEquipos que realmente quieren Kubernetes, Helm, Argo CD y GitOps en AWSAlta
Google Cloud RunEl nivel gratuito basado en solicitudes incluye 2 millones de solicitudes, 180,000 vCPU-segundos y 360,000 GiB-segundos; los ejemplos oficiales muestran 13.69/mes∗∗paraunaAPIde10Msolicitudesy∗∗13.69/mes** para una API de 10M solicitudes y **7.25/mes para un servicio ligero basado en eventosBaja a mediaAPIs sencillas en contenedores con escalado a cero y poca carga operativaBaja
Google GKETarifa de administración del clúster de 0.10/cluˊster−hora∗∗,aproximadamente∗∗0.10/clúster-hora**, aproximadamente **74.40/mes antes de los costos de nodos y redAltaEquipos que quieren GitOps en Kubernetes sobre Google CloudAlta

Qué significa esto en la práctica

Need Helm + Argo CD + Kubernetes?
  |
  +-- yes --> EKS or GKE
  |
  +-- no --> Want fastest path for a normal containerized API?
            |
            +-- yes --> Render, Railway, or Cloud Run
            |
            +-- no --> Need edge-first deployment and accept a different runtime model?
                      |
                      +-- yes --> Cloudflare Workers

Cloudflare

Cloudflare es la opción más interesante de esta lista cuando la respuesta es «depende».

Cloudflare ahora admite oficialmente FastAPI en Python Workers. Su documentación indica explícitamente que el paquete FastAPI es compatible con Python Workers y que el entorno de ejecución de Workers proporciona un servidor ASGI directamente al Worker.

Eso significa que Cloudflare puede ser muy atractivo cuando quieres:

  • una API distribuida globalmente
  • despliegues extremadamente sencillos
  • un costo inicial bajo
  • un modelo orientado al edge

Pero hay una salvedad arquitectónica:

  • este enfoque no está orientado naturalmente a Docker
  • no conserva de forma natural el flujo de Helm + Argo CD de este artículo
  • es posible que debas reconsiderar algunos supuestos sobre los paquetes de Python en comparación con un contenedor Linux convencional

Mi opinión:

  • elige Cloudflare Workers si quieres el entorno de ejecución de edge más sencillo y tu app de FastAPI encaja en el modelo de Workers
  • elige Cloudflare Containers si quieres un entorno de ejecución más parecido al de un contenedor en Cloudflare
  • no elijas Cloudflare solo porque el artículo usa GHCR, Helm y Argo CD, ya que Cloudflare destaca más cuando simplificas y prescindes de ese stack

Render

Render es probablemente la opción de «plataforma convencional para apps» más sencilla para este ejemplo concreto de FastAPI.

Por qué resulta atractivo:

  • complejidad muy baja de la plataforma
  • soporte sencillo para Docker
  • despliegues automáticos basados en Git
  • soporte para entornos de vista previa
  • bajo costo de entrada para apps pequeñas

Por qué está menos alineado con la arquitectura de este artículo:

  • normalmente no necesitas Helm allí
  • normalmente no necesitas Argo CD allí
  • GHCR puede seguir siendo útil, pero Render también puede construir y desplegar directamente desde Git o Docker

Mi opinión:

  • elige Render si tu prioridad es entregar rápidamente una app pequeña de FastAPI con una excelente experiencia de desarrollo
  • evita forzar patrones de Kubernetes en Render a menos que ya sepas que pronto necesitarás algo más grande

Railway

Railway es una alternativa muy sólida, especialmente si buscas algo tan sencillo como Render, pero con un modelo de precios más claramente basado en el uso.

Por qué resulta atractivo:

  • baja complejidad de la plataforma
  • facturación por uso con niveles de entrada sencillos
  • despliegues automáticos conectados a GitHub
  • una opción Wait for CI para que los despliegues esperen a que GitHub Actions termine correctamente
  • verificaciones de salud que esperan una respuesta 200 antes de cambiar el tráfico
  • soporte para registries privados, incluido GHCR

Lo que hace que Railway sea especialmente relevante para este artículo es que puede ocupar un punto intermedio:

  • es más sencillo que Kubernetes
  • ofrece más flexibilidad de despliegue que algunas plataformas que «solo publican desde Git»
  • puede descargar imágenes privadas de GHCR cuando tienes el plan Pro

Esto hace que Railway sea una de las alternativas más sólidas si tu workflow es:

  • construir la imagen en GitHub Actions
  • publicar la imagen en GHCR
  • desplegar esa imagen en una plataforma administrada de aplicaciones

Pero todavía hay limitaciones importantes:

  • Helm y Argo CD no son componentes naturales del workflow de Railway
  • descargar imágenes privadas de GHCR requiere el plan Pro
  • si tu servicio tiene un volumen asociado, Railway documenta que los redespliegues tendrán un breve tiempo de inactividad para evitar la corrupción de datos

Mi opinión:

  • elige Railway si quieres una buena experiencia de desarrollo, flexibilidad para desplegar con Docker o GHCR y no necesitas Kubernetes
  • elige Railway en lugar de Render si prefieres el modelo basado en uso o quieres específicamente un camino más directo desde una imagen en GHCR hasta el despliegue
  • no elijas Railway si el objetivo principal es conservar la arquitectura de Helm + Argo CD + Kubernetes de este artículo

AWS

AWS te ofrece dos opciones claras:

AWS App Runner

App Runner es el camino más sencillo.

Encaja bien cuando quieres:

  • AWS como proveedor de nube
  • una plataforma administrada de contenedores
  • no tener que operar un plano de control de Kubernetes
  • un camino más directo desde una imagen de Docker hasta un servicio en ejecución

Es una buena opción para una API sencilla de FastAPI, pero no es el lugar natural para Helm + Argo CD.

AWS EKS

EKS encaja mejor cuando la arquitectura del artículo es el objetivo real.

Elígelo cuando quieras:

  • Kubernetes como estándar de plataforma
  • Helm como contrato de configuración
  • Argo CD como reconciliador de GitOps
  • prepararte para múltiples servicios, namespaces, entornos y patrones de plataforma

La desventaja es el costo y la complejidad:

  • hay una tarifa de administración del clúster antes de sumar el cómputo
  • sigues pagando por nodos, balanceadores de carga, almacenamiento y servicios adicionales de AWS
  • la configuración y las operaciones de la plataforma son considerablemente más complejas que en App Runner

Mi opinión:

  • elige AWS App Runner para un servicio sencillo en AWS
  • elige AWS EKS solo cuando realmente quieras el modelo operativo de Kubernetes + GitOps

Google Cloud

Google Cloud tiene una división muy similar:

Cloud Run

Cloud Run es el camino más sencillo para una app simple de FastAPI.

Resulta atractivo cuando quieres:

  • desplegar contenedores sin operar Kubernetes
  • aprovechar los costos del escalado a cero
  • un camino rápido desde el código hasta el servicio en ejecución
  • un costo mensual mínimo más bajo para cargas de trabajo modestas

Para muchas APIs pequeñas, esta es la opción administrada más rentable de la comparación.

GKE

GKE encaja mejor cuando quieres el flujo completo de Kubernetes de este artículo.

Elígelo cuando quieras:

  • charts de Helm como contrato de despliegue de primera clase
  • GitOps con Argo CD
  • un modelo operativo estándar de Kubernetes en Google Cloud

Al igual que con EKS, la contrapartida es que el clúster tiene un costo de administración incluso antes de que comiencen los costos de tu carga de trabajo.

Mi opinión:

  • elige Cloud Run si quieres una plataforma de contenedores más sencilla
  • elige GKE cuando Kubernetes sea una decisión real de producto y no solo un detalle de implementación

Mi recomendación honesta según la etapa

  • Si se trata de un MVP o un servicio interno pequeño, elige primero Render, Railway o Cloud Run.
  • Si tu enfoque está en el edge y tu app de FastAPI encaja en el entorno de ejecución, vale la pena considerar seriamente Cloudflare Workers.
  • Si tu equipo ya opera Kubernetes y quiere GitOps, elige EKS o GKE.
  • Si la razón principal para considerar Kubernetes es «porque parece más preparado para producción», normalmente eso por sí solo no es suficiente.

Si decides usar Kubernetes, ¿qué opciones de infraestructura son comunes?

Si la conclusión es «sí, realmente queremos Kubernetes», la siguiente pregunta no es Helm ni Argo CD. La siguiente pregunta es sobre qué infraestructura de Kubernetes deberíamos ejecutar.

Para la mayoría de los equipos, la lista habitual de opciones se ve así:

OpciónTipoMejor opción paraMi opinión
Amazon EKSKubernetes administradoEquipos centrados en AWS que quieren una integración profunda con AWSUna opción sólida cuando el resto de tu plataforma ya está en AWS
Google GKEKubernetes administradoEquipos en Google Cloud que quieren una experiencia de Kubernetes administrado bien pulidaUna de las experiencias de Kubernetes administrado más completas si GCP ya es tu plataforma principal
Azure AKSKubernetes administradoEquipos centrados en Azure y organizaciones que usan ampliamente MicrosoftUna opción predeterminada muy razonable si tu empresa ya opera en Azure
DigitalOcean Kubernetes (DOKS)Kubernetes administradoEquipos más pequeños que quieren un Kubernetes administrado más sencillo y menos componentes de nube que aprenderUna buena opción si «quieres Kubernetes, pero no toda la complejidad de un hyperscaler»
K3s en VPS o bare metalKubernetes ligero autoadministradoLaboratorios, edge, plataformas internas, equipos sensibles al costo y equipos que realmente quieren control totalExcelente cuando quieres Kubernetes ligero y puedes asumir más trabajo de la plataforma

Cómo elegiría

Amazon EKS

Elige EKS cuando:

  • tu infraestructura ya depende en gran medida de AWS
  • quieres una integración nativa con los patrones de IAM, red, almacenamiento y observabilidad de AWS
  • los ingenieros de plataforma de tu equipo ya trabajan con los componentes básicos de AWS

EKS es una opción sólida por defecto para producción, pero trae consigo la carga habitual de operar una plataforma Kubernetes.

Google GKE

Elige GKE cuando:

  • tus cargas de trabajo ya están en Google Cloud
  • quieres una plataforma madura de Kubernetes administrado
  • tu equipo valora más una buena experiencia de Kubernetes lista para usar que las integraciones profundas específicas de AWS o Azure

Si ya elegiste Cloud Run para servicios más sencillos, GKE es el siguiente paso natural en Google Cloud cuando ese modelo ya no es suficiente.

Azure AKS

Elige AKS cuando:

  • tu organización está estandarizada en Azure
  • los estándares de identidad, red y operaciones ya giran en torno a las herramientas de Microsoft
  • quieres Kubernetes administrado sin salir del ecosistema de Azure

La documentación de AKS de Microsoft lo presenta como un servicio de Kubernetes administrado que reduce la carga operativa, y la documentación general señala explícitamente que Azure administra el plano de control por ti.

DigitalOcean Kubernetes

Elige DigitalOcean Kubernetes cuando:

  • quieres Kubernetes real, no solo una plataforma de contenedores
  • tu equipo es pequeño y quiere una plataforma de nube más sencilla
  • también quieres herramientas estándar de Kubernetes como kubectl, escalado automático, balanceadores de carga y volúmenes

La documentación de DigitalOcean describe DOKS como Kubernetes con un plano de control totalmente administrado, alta disponibilidad y escalado automático, además de integración con las herramientas estándar de Kubernetes. Eso lo convierte en un buen punto intermedio entre «la plataforma completa de un hyperscaler» y «supongo que ahora tenemos que administrarlo todo».

K3s en VPS o bare metal

Elige K3s cuando:

  • quieres Kubernetes ligero
  • estás desplegando en el edge, en un homelab, en hardware interno o en VMs sencillas
  • te importan más el control operativo y un menor costo directo de infraestructura que la comodidad de un servicio totalmente administrado

K3s se presenta explícitamente como Kubernetes ligero y encaja muy bien en edge, CI, desarrollo, computadoras de placa única, entornos aislados de la red y otras situaciones en las que Kubernetes completo sería excesivo.

La contrapartida es clara:

  • te encargas de una mayor parte del ciclo de vida del clúster
  • te encargas de más tareas de red, almacenamiento, actualizaciones y recuperación
  • solo deberías elegir este camino si quieres asumir esa responsabilidad de forma consciente

Mi lista práctica de opciones

  • Elige EKS si ya usas AWS.
  • Elige GKE si ya usas Google Cloud.
  • Elige AKS si tu empresa ya está comprometida con Azure.
  • Elige DigitalOcean Kubernetes si quieres Kubernetes administrado con una plataforma más sencilla.
  • Elige K3s solo cuando realmente quieras Kubernetes ligero autoadministrado.

Buenas prácticas que mejoran la experiencia de desarrollo sin perjudicar producción

  • Publica tanto un tag fácil de identificar como uno inmutable, pero despliega con el inmutable.
  • Mantén el desarrollo local en Compose. No exijas Kubernetes para cambiar un handler de ruta.
  • Usa docker compose up --watch para el ciclo de desarrollo local.
  • Mantén las herramientas opcionales que solo se usan localmente detrás de perfiles de Compose.
  • Coloca los valores predeterminados del chart en values.yaml y mantén las sobreescrituras de cada entorno en archivos explícitos envs/dev/values.yaml y envs/prod/values.yaml.
  • Permite que Argo CD sincronice dev automáticamente con frecuencia.
  • Trata las promociones a prod como eventos de release explícitos, idealmente con tags de versión semántica.
  • Evita dar a CI credenciales directas del clúster cuando Argo CD pueda obtener los cambios desde Git.
  • Si los artefactos de GHCR son privados, usa credenciales de descarga dedicadas para Argo CD y concede solo el acceso necesario.
  • Usa GITHUB_TOKEN de GitHub para publicar paquetes del mismo repo en Actions cuando sea posible.

Errores comunes

  • Tratar el registry como la fuente de verdad en lugar de Git.
  • Desplegar latest, main o dev directamente a prod.
  • Mezclar el versionado de charts y el de imágenes sin una política.
  • Usar Helm solo como un sistema de plantillas de texto, sin un contrato claro de valores.
  • Construir imágenes diferentes para local y el clúster sin entender cómo divergen.
  • Hacer que el desarrollo local dependa de toda la plataforma de producción.

Mi configuración base recomendada

Si quieres una respuesta práctica, es esta:

  1. Mantén el desarrollo local de FastAPI en Docker Compose con watch.
  2. Publica las imágenes de la aplicación en ghcr.io/<owner>/<repo>.
  3. Publica los charts OCI de Helm en oci://ghcr.io/<owner>/charts.
  4. Mantén los manifiestos Application de Argo CD y los valores de entorno en un repo de GitOps.
  5. Al hacer merge a main, publica automáticamente una imagen y un chart de dev y luego actualiza dev automáticamente.
  6. Con los tags vX.Y.Z, publica automáticamente una imagen y un chart de release y luego promociónalos a prod.

Es lo bastante sencillo para un equipo pequeño, lo bastante claro para que los PMs lo sigan y lo bastante sólido para crecer con el proyecto.

Fuentes

Verificado el April 8, 2026 con documentación primaria:

Algunas recomendaciones de este artículo son inferencias de ingeniería basadas en la documentación anterior. En particular, la recomendación de mantener Git como fuente de verdad persistente para los despliegues, mientras se usa GHCR como registry de artefactos tanto para imágenes como para charts, es una decisión arquitectónica y no un requisito directo de los proveedores.