Cargos inesperados por egress: de dónde salen las facturas de bandwidth
Los cargos inesperados por egress suelen deberse a cuatro hábitos: servir media desde la app, no usar cache headers, APIs demasiado verbosas y tráfico entre regiones. Encuentra el origen y reduce la factura sin cambiar de proveedor.
Llega la factura y es cuatro veces superior a lo que esperabas. La línea de compute se acerca bastante a lo que habías presupuestado. La que no encaja es la de egress —los bytes que salen de tu infraestructura— y es muy posible que nunca hayas pensado en ello.
Los cargos inesperados por egress son la sorpresa más habitual en la facturación del hosting de aplicaciones, y el motivo es estructural: nada en tu ciclo de desarrollo mide los bytes. Tu portátil no te cobra por ellos. Staging no tiene usuarios. La primera vez que el bandwidth se hace visible es en una factura y, para entonces, ya has puesto en producción el hábito que lo provocó.
Esto es de donde sale realmente, ordenado según la frecuencia con la que suele ser la causa.
1. Servir media desde la aplicación
Este caso eclipsa a los demás y casi siempre es accidental.
Colocas un vídeo en /public para hacer una demo. Funciona, así que se queda ahí. Ahora cada visita a la página transmite ese archivo desde tu container. Un vídeo de 40 MB en una página con mil visitantes al mes supone 40 GB, procedentes de un archivo que considerabas provisional.
Lo mismo ocurre con las imágenes de producto, los PDF, las subidas de usuarios y los archivos de fuentes. Cuando los añades, ninguno parece una decisión de infraestructura.
La solución no tiene misterio: la media estática debe estar en object storage detrás de una CDN, no en el filesystem de tu aplicación. Tu app debería servir HTML y JSON. En el momento en que sirve una imagen principal de 4 MB a cada visitante, está haciendo el trabajo de una CDN sin beneficiarse de ninguno de sus costes.
2. Ausencia de cache headers
Si tus respuestas no tienen cache headers, cada visitante descarga todos los assets en cada carga de página. Los visitantes recurrentes vuelven a descargarlo todo. Un crawler lo descarga decenas de veces al día.
# Fingerprinted build assets never change — cache them for a year
Cache-Control: public, max-age=31536000, immutable
# HTML changes — revalidate but allow a short window
Cache-Control: public, max-age=0, must-revalidate
# Anything user-specific
Cache-Control: private, no-store
Es un cambio de una línea por clase de respuesta que suele eliminar la mayor parte de una factura de bandwidth, porque el tráfico que evita es pura repetición.
3. APIs que devuelven más de lo que usa el cliente
Un endpoint de listado que devuelve objetos completos cuando la UI muestra tres campos está enviando la diferencia en cada request, para siempre. Nunca aparece en el profiling porque es rápido: simplemente es grande.
Hay dos cosas que debes comprobar en tus endpoints con más tráfico:
- ¿Está activada la compresión?
Content-Encoding: gzipobren JSON suele reducir el tamaño entre un 70 y un 80 %. Es una línea de middleware y con frecuencia falta. - ¿Estás enviando campos que nadie lee? Serializar la fila completa porque el ORM lo facilita es el comportamiento predeterminado de la mayoría de los codebases.
Un cliente que hace polling empeora ambas cosas en función del número de veces por minuto que consulta.
4. Tráfico entre regiones y entre servicios
Si tu app está en una región y tu base de datos en otra, cada resultado de una query cruza un límite de pago. Es fácil hacerlo accidentalmente: creas primero la base de datos, eliges una región sin pensarlo demasiado y colocas la app en otro sitio semanas después.
El tráfico entre tus propios servicios también puede contar como egress si sale de la red privada y vuelve a través de un hostname público. Dos servicios del mismo workspace que se comunican mediante sus URLs públicas están pagando dos veces por una conversación que nunca necesitó salir al exterior.
En Dockup es difícil cometer este error concreto, porque los servicios se direccionan mediante su nombre interno. Un servicio llega a main-db.internal:5432 y a otro servicio mediante su alias <slug>.internal: el tráfico que permanece dentro de la red del workspace nunca pasa por un listener público. Por eso la base de datos tampoco tiene un hostname público de forma predeterminada: no hay nada que se pueda enrutar accidentalmente a través del exterior.
Encontrarlo en lugar de adivinar
Adivinar cuál de los cuatro casos es el responsable hace perder un ciclo de facturación. Mídelo:
Lee el access log por bytes, no por cantidad. Ordena las respuestas principales por el total de bytes servidos, no por el número de requests. El endpoint que más datos envía rara vez es el que más veces se llama.
Comprueba tus assets estáticos más grandes. Si algo de más de un megabyte se está sirviendo desde tu app, es lo primero que deberías mover.
# What is the service actually doing right now
dockup metrics my-project/my-api --json
# And what is it logging
dockup logs my-project/my-api -n 1000
Vigila los crawlers. Un bot con un comportamiento incorrecto que descargue archivos grandes repetidamente puede representar la mayor parte del egress de un mes en un sitio con un tráfico humano moderado. robots.txt y un noindex en los entornos de preview son medidas sencillas.
Los entornos que nadie contabiliza
Dos hábitos generan egress que nunca aparece en el modelo mental de nadie:
Entornos de preview. Un preview por pull request es realmente útil y multiplica silenciosamente tu footprint. Cinco PR abiertas son cinco copias de tu stack, cada una con su propio tráfico, a menudo procedente de los mismos crawlers, porque las URLs de preview se indexan si no lo evitas.
Staging de larga duración. Un entorno de staging que lleva ocho meses activo y que nadie visita sigue descargando imágenes, sigue recibiendo crawlers y sigue ejecutando sus cron jobs.
Ninguna de las dos cosas es motivo para dejar de usarlos. Ambas son razones para ponerles límites: elimina los previews cuando se cierre la PR y márcalos con noindex para que los motores de búsqueda dejen de descargarlos en tu nombre.
dockup noindex my-project/my-api --on
Cómo es una buena facturación
Deberías poder responder a tres preguntas antes de que llegue la factura:
- ¿Qué está funcionando ahora mismo? Incluidas las cosas que habías olvidado.
- ¿Qué ha consumido cada elemento? Separando compute y transfer, no en una única cifra.
- ¿Qué ocurre si se dispara el consumo? Un límite que detiene los servicios es desagradable. Uno que no existe es peor.
Dockup contabiliza la infraestructura por separado del plan, de modo que la cuota del plan no oculta el uso; además, el crédito de 20 $ del plan Pro se aplica sobre ese uso, en lugar de ser un descuento sobre la cuota. La idea es que puedas ver qué corresponde a cada concepto.
La versión corta
Mueve la media a object storage. Configura cache headers. Activa la compresión. Mantén los servicios y sus bases de datos en una misma región y deja que se comuniquen a través de la red privada, no de la internet pública. Elimina los entornos de preview cuando caduquen y mantenlos fuera de los resultados de búsqueda.
Estos cinco hábitos resuelven casi todas las facturas de bandwidth inesperadas y ninguno requiere cambiar el lugar donde haces hosting.
Preguntas frecuentes
¿Qué es el egress?
Son los datos que salen de tu infraestructura hacia internet: respuestas a usuarios, archivos descargados y payloads de API. El tráfico entrante suele ser gratuito; el saliente, normalmente no.
¿Por qué mi factura de egress es más alta que la de compute?
Casi siempre porque la aplicación está sirviendo archivos que deberían estar en una CDN. Un único asset grande en una página popular puede superar el coste de semanas de gestión de requests.
¿Una CDN realmente lo reduce?
Sí, cuando hace cache. El origin sirve el archivo una vez por edge, en lugar de una vez por visitante, y el precio por gigabyte de la CDN suele ser una fracción del de una application platform.
¿Los entornos de preview cuestan tanto como producción?
Cuestan en función de lo que consumen, que suele ser más de lo esperado porque cada uno es una copia completa y las URLs de preview reciben crawlers si no lo impides.
