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.ioes 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
mainy 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 comov1.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:
| Artefacto | Ejemplo |
|---|---|
| Imagen de Docker | ghcr.io/acme/hello-fastapi:sha-abc1234 |
| Tag de imagen fácil de identificar | ghcr.io/acme/hello-fastapi:main |
| Chart OCI de Helm | oci://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:
watchmejora el ciclo local automatizado para servicios construidos desde el código fuenteprofilespermite 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 pushinfiere el nombre del chart a partir deChart.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
chartde Helm para charts OCI,repoURLno incluye el prefijooci://. - 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:
| Entorno | Disparador | Qué se construye | Qué despliega Argo CD |
|---|---|---|---|
| local | docker compose up --watch | solo la imagen local | nada en el clúster |
| dev | merge a main | imagen + paquete de chart para dev | versión exacta del chart de dev y tag exacto de la imagen en Git |
| prod | push de un tag como v1.2.0 | imagen de release + paquete de chart de release | versió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:
- Se hace merge de un PR a
main. - GitHub Actions construye la imagen.
- El workflow publica la imagen en GHCR con:
- un tag inmutable
sha-<commit> - un tag móvil
mainopcional, fácil de identificar
- un tag inmutable
- El workflow empaqueta una versión del chart para dev como
0.1.0-dev.128. - El workflow publica el chart en GHCR.
- El workflow actualiza el repo de GitOps para
dev. - Argo CD sincroniza
devautomá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:
- obtener
1.2.0a partir dev1.2.0 - construir y publicar
ghcr.io/acme/hello-fastapi:1.2.0 - publicar también
sha-<commit>para trazabilidad - empaquetar la versión
1.2.0del chart - publicar el chart en GHCR
- actualizar el repo de GitOps para
prod - 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
digestpara tags mutables comomain - la estrategia
semverpara tags de release - escritura en Git para que las actualizaciones se guarden allí en lugar de modificar solo la
Applicationactiva
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:
- «¿Cuál es el lugar más fácil y económico para ejecutar una app sencilla de FastAPI?».
- «¿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ón | Referencia de costo mensual según la documentación oficial | Complejidad | Mejor opción para | Compatibilidad con GHCR + Helm + Argo CD |
|---|---|---|---|---|
| Cloudflare Workers | El 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 Workers | Baja a media | APIs orientadas al edge y apps ligeras de FastAPI compatibles con el modelo de Workers | Baja |
| Cloudflare Containers | Incluido 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ón | Media | Cargas de trabajo en contenedores en Cloudflare cuando quieres un entorno de ejecución más parecido al de un contenedor que Workers | Baja a media |
| Render Web Service | El workspace Hobby cuesta 0 y el primer nivel de pago para servicios web cuesta $7/mes | Baja | La experiencia de desarrollo más rápida para un equipo pequeño que entrega una API en Docker | Baja |
| Railway | Free cuesta 5/mes e incluye 20/mes e incluye $20 de uso. Los registries privados como GHCR requieren Pro | Baja | Equipos pequeños que buscan una buena experiencia de desarrollo, facturación por uso, flexibilidad de Docker y despliegues opcionales de imágenes de GHCR | Baja a media |
| AWS App Runner | Los ejemplos oficiales muestran 25.50/mes para una API ligera sensible a la latencia | Media | Equipos que ya usan AWS y quieren contenedores administrados sin operar Kubernetes | Baja |
| AWS EKS | 73/mes antes de los nodos de trabajo y otros recursos de AWS | Alta | Equipos que realmente quieren Kubernetes, Helm, Argo CD y GitOps en AWS | Alta |
| Google Cloud Run | El nivel gratuito basado en solicitudes incluye 2 millones de solicitudes, 180,000 vCPU-segundos y 360,000 GiB-segundos; los ejemplos oficiales muestran 7.25/mes para un servicio ligero basado en eventos | Baja a media | APIs sencillas en contenedores con escalado a cero y poca carga operativa | Baja |
| Google GKE | Tarifa de administración del clúster de 74.40/mes antes de los costos de nodos y red | Alta | Equipos que quieren GitOps en Kubernetes sobre Google Cloud | Alta |
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 CIpara que los despliegues esperen a que GitHub Actions termine correctamente - verificaciones de salud que esperan una respuesta
200antes 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ón | Tipo | Mejor opción para | Mi opinión |
|---|---|---|---|
| Amazon EKS | Kubernetes administrado | Equipos centrados en AWS que quieren una integración profunda con AWS | Una opción sólida cuando el resto de tu plataforma ya está en AWS |
| Google GKE | Kubernetes administrado | Equipos en Google Cloud que quieren una experiencia de Kubernetes administrado bien pulida | Una de las experiencias de Kubernetes administrado más completas si GCP ya es tu plataforma principal |
| Azure AKS | Kubernetes administrado | Equipos centrados en Azure y organizaciones que usan ampliamente Microsoft | Una opción predeterminada muy razonable si tu empresa ya opera en Azure |
| DigitalOcean Kubernetes (DOKS) | Kubernetes administrado | Equipos más pequeños que quieren un Kubernetes administrado más sencillo y menos componentes de nube que aprender | Una buena opción si «quieres Kubernetes, pero no toda la complejidad de un hyperscaler» |
| K3s en VPS o bare metal | Kubernetes ligero autoadministrado | Laboratorios, edge, plataformas internas, equipos sensibles al costo y equipos que realmente quieren control total | Excelente 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 --watchpara 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.yamly mantén las sobreescrituras de cada entorno en archivos explícitosenvs/dev/values.yamlyenvs/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_TOKENde 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,mainodevdirectamente 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:
- Mantén el desarrollo local de FastAPI en Docker Compose con
watch. - Publica las imágenes de la aplicación en
ghcr.io/<owner>/<repo>. - Publica los charts OCI de Helm en
oci://ghcr.io/<owner>/charts. - Mantén los manifiestos
Applicationde Argo CD y los valores de entorno en un repo de GitOps. - Al hacer merge a
main, publica automáticamente una imagen y un chart de dev y luego actualizadevautomáticamente. - Con los tags
vX.Y.Z, publica automáticamente una imagen y un chart de release y luego promociónalos aprod.
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:
- GitHub Docs: Trabajar con el Container registry
- GitHub Docs: Publicar imágenes de Docker
- GitHub Docs: Eventos que activan workflows
- Docker Docs: Usar Compose Watch
- Docker Docs: Usar perfiles con Compose
- Docker Docs: Controlar el orden de inicio y apagado en Compose
- Docker Docs: Interpolación en Compose
- Helm Docs: Usar registries basados en OCI
- Argo CD Docs: Helm
- Argo CD Docs: OCI
- Argo CD Docs: Política de sincronización automática
- Argo CD Docs: Configuración declarativa para registries privados de Helm y OCI
- Argo CD Image Updater Docs: Métodos de actualización
- Argo CD Image Updater Docs: Estrategias de actualización
- Cloudflare Workers Docs: Precios
- Cloudflare Workers Docs: FastAPI en Python Workers
- Render: Precios
- Railway Docs: Precios
- Railway Docs: Planes de precios
- Railway Docs: Registries privados
- Railway Docs: Controlar los despliegues automáticos de GitHub
- Railway Docs: Verificaciones de salud
- Precios de AWS App Runner
- Precios de Amazon EKS
- Amazon EKS: ¿Qué es Amazon EKS?
- Precios de Google Cloud Run
- Documentación de Google Kubernetes Engine
- Azure Kubernetes Service: ¿Qué es AKS?
- Precios de Azure Kubernetes Service
- Inicio rápido de DigitalOcean Kubernetes
- DigitalOcean Kubernetes: Elementos administrados
- Precios de Google Kubernetes Engine
- K3s - Kubernetes ligero
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.