2026-10-25 09:00:00+00:00

The Hidden Cost of Always-On AWS EC2 & Application Load Balancers

For years, this website (aldianfazrihady.com) ran on a classic high-availability AWS architecture: CloudFront CDN in front of an internet-facing Application Load Balancer (AFCOM-0), an Auto Scaling Group of EC2 Spot Instances running Nginx + uWSGI (for HTTP) + Daphne (for WebSockets) + Celery, RDS PostgreSQL, and ElastiCache Redis.

Even after aggressive optimizations—such as pruning unused ALB availability zones to minimize AWS's $0.005/hr public IPv4 charges and automating AMI snapshot cleanup—an always-on AWS ALB + EC2 + Elastic IP stack still carries a baseline floor of ~$45.70/month ($25 ALB + $17 EC2 + $3.70 IPv4) regardless of traffic volume.

By migrating the entire stack to Google Cloud Run serverless containers with Daphne ASGI, WhiteNoise, Supabase PostgreSQL, and Redis Cloud, we reduced baseline infrastructure cost to $0.00/month while preserving real-time WebSocket chat, push notifications, and sub-second responses.


1. Consolidating Nginx, uWSGI, and Daphne into a Single ASGI Port

On traditional VMs, Django deployments often split HTTP traffic (uWSGI/Gunicorn) and WebSocket traffic (Daphne/Uvicorn) across separate internal ports behind Nginx. On Google Cloud Run, a container exposes a single ingress PORT (default 8080) that natively supports both HTTP/2 and WebSocket (Upgrade: websocket) connections with up to a 60-minute request timeout.

# Dockerfile entrypoint running Daphne ASGI on $PORT
CMD daphne -b 0.0.0.0 -p ${PORT:-8080} afcom.routing:application

To serve compiled Webpack bundles and Service Worker scripts without a separate Nginx container, we integrated WhiteNoise (whitenoise.middleware.WhiteNoiseMiddleware) with a custom IMMUTABLE_FILE_TEST and a Service-Worker-Allowed: / header hook so the PWA service worker can control the root scope even when served from /static/afcom/bundles/.


2. Eliminating Celery Workers in Serverless Containers

Scale-to-zero containers cannot rely on long-running background Celery daemon processes because CPU is throttled when no HTTP or WebSocket request is active. We refactored two critical paths:

  1. Real-Time Admin-to-Visitor Chat Replies: Instead of dispatching a Celery task to push messages over the channel layer, the REST endpoint calls async_to_sync(channel_layer.group_send) directly against Redis Cloud, delivering replies to the visitor's WebSocket in <15ms.
  2. Email Notifications: Dispatched via non-blocking thread execution during the active request lifecycle using Gmail SMTP TLS.

3. Automated CI/CD with Google Cloud Build

Every push triggers a reproducible multi-step build via cloudbuild.yaml that builds the container image, tags it with $SHORT_SHA, pushes it to Artifact Registry / GCR, and deploys a new Cloud Run revision with GIT_HASH=$SHORT_SHA injected as an environment variable.


About the Author — Hire Aldian Fazrihady

Aldian Fazrihady is a Top-Rated Freelance Full Stack Developer and AI Application Engineer with 15+ years of production software engineering experience and Google Cloud Professional Cloud Architect & Data Engineer certifications. He specializes in building custom LLM & autonomous AI agents, RAG pipelines, Next.js/React PWAs, Python (Django/FastAPI), Go microservices, and serverless cloud infrastructure on Google Cloud Run and AWS.

Hire Aldian Fazrihady on Upwork | View Live AI Portfolio | Send a Direct Message