Service topology & runtime health.
Django REST API and Next.js SSR behind NGINX on a single EC2 instance in us-east-1. Celery workers consume from RabbitMQ for async task processing; Redis handles cache and session state; PostgreSQL is the persistent store.
┌─────────────────────────────────────────────┐
│ AWS EC2 us-east-1 │
└──────────────────┬─────────────────────────┘
│
┌──────────────▼──────────────┐
│ NGINX │ ← SSL / reverse proxy
│ levrg.tech │
└────────┬────────────┬───────┘
│ │
┌────────────▼──┐ ┌─────▼────────────┐
│ Next.js │ │ Django REST API │
│ SSR frontend │ │ + WebSocket │
└───────────────┘ └─────┬────────────┘
│
┌─────────────────────────┼──────────────────────┐
│ │ │
┌──▼────────────────┐ ┌────▼────────────┐ ┌────▼────────────┐
│ Celery workers │ │ RabbitMQ broker │ │ Redis cache │
│ ETL · ML tasks │ │ message queue │ │ sessions · TTL │
└─────────────────┘ └──┬─────────────┘ └─────────────────┘
│
┌────────▼────────────┐
│ PostgreSQL │
│ primary data store │
└─────────────────────┘NGINX handles TLS termination and reverse-proxies to Next.js for SSR requests or Django for REST and WebSocket endpoints. Compute-heavy and scheduled work is dispatched to Celery workers via RabbitMQ. Redis stores short-lived cache entries and session data; PostgreSQL is the durable store for all structured data.
| Service | Status | P95 | Uptime |
|---|---|---|---|
| nginx | ● up | 2ms | 100% |
| nextjs · ssr | ● up | 86ms | 99.99% |
| django · api | ● up | 124ms | 99.97% |
| celery · etl | ● up | — | 99.92% |
| celery · ml | ● degraded | — | 98.41% |
| rabbitmq | ● up | 8ms | 99.99% |
| redis | ● up | 1ms | 100% |
| postgres | ● up | 14ms | 99.98% |
P95 latency is a more useful health signal than mean latency — it captures the tail of the distribution rather than the center, which is where user-facing degradation actually occurs. The degraded ML Celery worker indicates inference tasks are either queuing behind other work or returning errors intermittently.
Six hours of CPU utilization across both vCPUs. Sustained load above 85% introduces scheduling contention across co-located containers. The periodic spikes correlate with Celery beat dispatching scheduled ETL and ML inference tasks.
71% average on a 4 GiB instance leaves limited headroom. Once the OOM threshold is reached, Linux begins terminating processes by OOM score — typically Redis first (dropping cache entries), followed by worker restarts and lost database connections.
Inbound is dominated by Celery pulling external API data (FRED, Alpha Vantage, Binance). Outbound is JSON serialized to browser clients. An asymmetric inbound spike without a corresponding outbound rise indicates a scheduled sync task ran without triggering user-facing requests.
The full runtime inventory of the production environment. Exact versions matter when auditing CVEs, reproducing dependency conflicts, or tracking transitive package relationships. Currently maintained manually — auto-syncing from the container image manifest is a planned improvement.
Celery beat drives the scheduled ETL — FRED syncs at 02:00 UTC nightly, Zillow ZHVI on the 17th after the monthly release, and Alpha Vantage equity quotes at 13:35 and 17:00 UTC (market open + midday). Writes are upserted; unchanged rows are skipped. Alpha Vantage results are cached in Redis for 12 hours and served via the Django API — the free tier allows 25 req/day, so 12 symbols are synced with a 13-second delay between calls. Binance and Alternative.me feeds bypass the backend entirely: Next.js fetches them at the edge and serves stale-while-revalidate via ISR, eliminating the database round-trip for high-frequency data. FRED publishes mortgage rates weekly on Thursdays (PMMS), treasury yields daily at 4:15pm ET (H.15 release), and the Fed Funds target 8× per year at FOMC meetings.