Ecstaticloud
Initializing0%
Ecstaticloud Logo
Back to Insights
AI & Cloud InfrastructureSeptember 16, 2026

Architecting Real-Time RAG: Low-Latency Inference with vLLM, Ray, and Serverless Vector DBs on AWS

Discover how to build and scale sub-100ms Retrieval-Augmented Generation (RAG) pipelines using vLLM, Ray clusters, and serverless vector databases on AWS. Learn key architectural patterns for GPU memory optimization, streaming tokens, and dynamic autoscaling in high-throughput enterprise environments.

In high-throughput enterprise applications, standard Retrieval-Augmented Generation (RAG) implementations often fall short due to compounding latencies. When your architecture relies on naive chains—a sequential pass through vector search, prompt assembly, and standard LLM inference engines—Time-To-First-Token (TTFT) frequently breaches 1,500ms. For real-time applications such as copilots, live customer support agents, or high-frequency automated decision engines, this is unacceptable.

To achieve sub-100ms TTFT and sustain thousands of concurrent streams without running up an unmanageable AWS bill, you need to abandon legacy orchestrators and synchronous architectures. You must refactor your stack around zero-copy data passing, optimized memory management, concurrent pipeline stages, and dynamically scaled accelerator infrastructure.

This guide outlines the reference architecture for building an enterprise-grade, ultra-low-latency RAG pipeline on AWS using vLLM, Ray Serve, and Serverless Vector Databases.


Architectural Blueprint

The core philosophy of this architecture is concurrency and zero-copy parallelism. Instead of waiting for tasks to execute sequentially, execution is interleaved, vectorized, and streamed.

                     +---------------------------------------+
                     |         Client Application            |
                     +---------------------------------------+
                                         |  (HTTP/2 SSE or WebSockets)
                                         v
                     +---------------------------------------+
                     |       AWS Network Load Balancer       |
                     +---------------------------------------+
                                         |
                                         v
                     +---------------------------------------+
                     |        EKS Cluster (Ingress API)      |
                     +---------------------------------------+
                                   /           \
                 (Async Embedding) /             \ (Prefetch Context)
                                 v                v
      +---------------------------------+   +-----------------------------------+
      |   Local Embedding Service       |   |  Serverless Vector DB             |
      |   (TensorRT / ONNX Runtime)     |   |  (Pinecone / Qdrant Serverless)   |
      +---------------------------------+   +-----------------------------------+
                                   \             /
                                    v           v
                     +---------------------------------------+
                     |            Ray Serve Router           |
                     |         (Ray Cluster on AWS)          |
                     +---------------------------------------+
                                         |
                       (IPC / Shared Memory / gRPC)
                                         v
                     +---------------------------------------+
                     |        vLLM Worker Instances          |
                     |    (PagedAttention + Continuous)      |
                     |     [AWS EC2 g5/p4d Instances]       |
                     +---------------------------------------+

Request Lifecycle Flow

  1. Ingress Layer: AWS Network Load Balancer (NLB) terminates TLS and routes HTTP/2 streaming connections directly to an API gateway running in Amazon EKS.
  2. Asynchronous Vector Search: The gateway uses a lightweight, in-memory CPU embedding worker (e.g., ONNX Runtime initialized with bge-micro-v2 or all-MiniLM-L6-v2) to convert the query into a dense vector in <8ms.
  3. Serverless Vector DB Query: The embedding vector is sent via gRPC to a Serverless Vector Database (e.g., Pinecone Serverless or Qdrant Cloud on AWS AWS Marketplace), returning top-$k$ document chunks over an warm connection pool in <25ms.
  4. Context Ingestion & Prompt Assembly: The Ray Serve head node receives metadata, formats the dynamic prompt, and dispatches the tensor state to the nearest vLLM worker using Ray’s zero-copy shared memory plasma store.
  5. vLLM Engine Processing: vLLM generates tokens using continuous batching and PagedAttention, firing the first token (TTFT) in <40ms.
  6. Streaming Back to Client: Tokens stream back via Server-Sent Events (SSE) through the NLB, delivering an end-to-end TTFT under the 100ms threshold.

Sub-100ms Subsystems Breakdown

1. The Embedding & Vector Retrieval Engine

To keep retrieval under 35ms total budget, eliminate external embedding API calls (like OpenAI embeddings) which introduce unpredictable multi-region network hops.

  • Local On-Instance Embeddings: Run quantized embedding models (FP16 or INT8) directly in memory on your ingress nodes using ONNX Runtime or TensorRT. Keep these models co-located with the ingestion engine to avoid network calls entirely.
  • gRPC and Keep-Alive Connections: Use gRPC for connection pooling with your Serverless Vector DB. HTTP/1.1 TLS handshakes add 20–40ms per call; maintaining persistent HTTP/2 gRPC channels removes this tax.
  • Metadata Filtering Optimization: Store pre-tokenized text or compressed context strings directly inside the vector database metadata payload. This eliminates the need to fetch raw document text from Amazon S3 or DynamoDB during the critical execution path.

2. vLLM: Eliminating Memory Bottlenecks with PagedAttention

Traditional LLM serving frameworks fail under scale because the Key-Value (KV) cache grows dynamically and requires contiguous GPU memory blocks. This causes severe memory fragmentation, limiting throughput and inflating latency.

vLLM solves this using PagedAttention:

  • Paged KV Cache: Virtual memory management translates logical KV blocks into physical allocations, completely eliminating external fragmentation.
  • Continuous Batching: Requests are batched at the iteration level rather than the sequence level. New requests enter the execution queue immediately on the next forward pass without waiting for previous requests to finish generation.
  • Chunked Prefill: Long context windows (a common bottleneck in RAG) are split into chunks. vLLM computes prefill chunks alongside generation tokens from other requests in the same batch, preventing prefill operations from blocking ongoing streaming generations.
Standard Attn:  [Req 1 Prefill................] [Req 1 Gen] [Req 1 Gen]
Continuous:     [Req 1 Prefill Chunk 1] [Req 2 Gen] [Req 1 Prefill Chunk 2] [Req 1 Gen]

3. Ray Serve: Distributed Dynamic Orchestration

Ray Serve bridges the gap between client API routing and GPU worker nodes. It provides the low-overhead, asynchronous actor model needed to manage high-throughput vLLM deployments across multi-GPU nodes.

  • Replicas & Actor Scaling: Ray creates long-lived Python worker processes on your GPU instances. This avoids process creation overhead and keeps the vLLM engine instance warm.
  • Request Multiplexing: Ray Serve routes incoming streams to vLLM dynamic dynamic batching queues based on live replica workload telemetry, avoiding queue pileups on individual GPUs.
  • Pipeline Parallelism: On larger models (e.g., Llama-3-70B), Ray orchestrates Tensor Parallelism ($TP$) across multiple GPUs on a single instance (e.g., 4x A10G on g5.12xlarge) or Pipeline Parallelism ($PP$) across instances via AWS Elastic Fabric Adapter (EFA).

Implementation: High-Throughput Ray + vLLM Pipeline

Below is a production-ready implementation of a low-latency, streaming RAG deployment utilizing Ray Serve and vLLM's AsyncLLMEngine.

import asyncio
from typing import AsyncGenerator
import json

from ray import serve
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine
from vllm.sampling_params import SamplingParams
from vllm.utils import random_uuid

@serve.deployment(
    num_replicas=2,
    ray_actor_options={"num_gpus": 1, "num_cpus": 4},
    max_ongoing_requests=256
)
class LowLatencyRAGEngine:
    def __init__(self, model_path: str):
        # Configure vLLM engine for maximum KV-cache efficiency and chunked prefill
        engine_args = AsyncEngineArgs(
            model=model_path,
            tensor_parallel_size=1,
            gpu_memory_utilization=0.90,
            max_num_batched_tokens=4096,
            enable_chunked_prefill=True,
            max_model_len=8192,
            trust_remote_code=True,
            dtype="float16"
        )
        self.engine = AsyncLLMEngine.from_engine_args(engine_args)

    async def _retrieve_context(self, query: str) -> str:
        """
        Simulated ultra-low latency serverless vector retrieval.
        In production: Use gRPC client pool to Pinecone/Qdrant.
        """
        # Execute retrieval asynchronously
        await asyncio.sleep(0.018)  # ~18ms lookup latency
        return "AWS EFA provides up to 100 Gbps network bandwidth per OS instance for OS bypass scale."

    @serve.batch(max_batch_size=16, batch_wait_timeout_s=0.005)
    async def process_batch_retrieval(self, queries: list[str]) -> list[str]:
        # High-density batch retrieval logic can be placed here if needed
        pass

    async def generate_stream(self, query: str) -> AsyncGenerator[str, None]:
        # Step 1: Parallel retrieval phase
        context_task = asyncio.create_task(self._retrieve_context(query))
        
        # Step 2: Assemble prompt immediately when context returns
        context = await context_task
        prompt = (
            f"<|system|>\nYou are an expert assistant. Context:\n{context}</s>\n"
            f"<|user|>\n{query}</s>\n<|assistant|>\n"
        )

        # Step 3: Configure sampling for low-latency generation
        sampling_params = SamplingParams(
            temperature=0.1,
            top_p=0.95,
            max_tokens=256,
            ignore_eos=False
        )

        request_id = f"rag-{random_uuid()}"
        
        # Step 4: Stream tokens directly from vLLM AsyncLLMEngine
        results_generator = self.engine.generate(
            prompt, 
            sampling_params, 
            request_id
        )

        previous_text = ""
        async for request_output in results_generator:
            text_chunk = request_output.outputs[0].text
            # Calculate stream diff
            delta = text_chunk[len(previous_text):]
            previous_text = text_chunk
            
            # Form Server-Sent Event payload
            payload = json.dumps({"token": delta, "finished": request_output.finished})
            yield f"data: {payload}\n\n"

    async def __call__(self, request) -> StreamingResponse:
        # FastAPI / Starlette request wrapper for SSE stream
        from starlette.responses import StreamingResponse
        
        body = await request.json()
        query = body.get("query", "")
        
        return StreamingResponse(
            self.generate_stream(query),
            media_type="text/event-stream"
        )

# Entry point for Ray application building
entrypoint = LowLatencyRAGEngine.bind(model_path="meta-llama/Meta-Llama-3-8B-Instruct")

AWS Infrastructure & Autoscaling Topology

Running high-performance models requires elastic infrastructure. Standard EC2 Auto Scaling Groups are too slow when provisioning GPU instances—pulling a 15GB model over standard networks can cause 10-minute cold starts.

EKS, KubeRay, and Karpenter Blueprint

We deploy our Ray cluster using the KubeRay Operator on Amazon EKS, paired with Karpenter for fast, dynamic NodePool provisioning using AWS instance types with fast local NVMe storage (g5.xlarge, g5.12xlarge, p4d.24xlarge).

apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: gpu-inference-nodepool
spec:
  template:
    spec:
      requirements:
        - key: karpenter.k8s.aws/instance-family
          operator: In
          values: ["g5"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
      nodeClassRef:
        name: gpu-node-class
  limits:
    resources:
      gpu: "32"
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 300s
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
  name: gpu-node-class
spec:
  amiFamily: AL2
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 200Gi
        volumeType: gp3
        iops: 10000
        throughput: 500
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: ecstaticloud-cluster
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: ecstaticloud-cluster

Optimizing Provisioning Times

  1. Container Image Pre-warming: Build custom container images with pre-installed vllm, ray, torch, and CUDA dependencies. Store these images in Amazon ECR with AWS Fargate/Container Read-Through Caching or use warm-node pools.
  2. Model Cache on Instance Storage: Mount fast local instance store NVMe SSDs (/dev/nvme0n1) for Hugging Face model weights. Avoid downloading weights directly over EBS at runtime.
  3. AWS Elastic Fabric Adapter (EFA): Enable EFA network interfaces on g5.12xlarge or p4d.24xlarge nodes to allow low-latency, direct GPU-to-GPU communication across instances for large-scale multi-node tensor parallelism.

Latency Budget Breakdown (Target: <100ms TTFT)

Achieving sub-100ms TTFT requires setting strict performance boundaries for every step in the pipeline:

| Pipeline Stage | Strategy / Technology | Alloc. Target | Realized Latency | | :--- | :--- | :--- | :--- | | Ingress Overhead | AWS NLB + EKS Ingress Controller | < 5ms | 3ms | | Embedding Generation| Quantized local ONNX on CPU / GPU | < 12ms | 8ms | | Vector DB Search | Persistent gRPC connection to Serverless DB | < 30ms | 22ms | | Payload Assembly | Ray zero-copy plasma store & prompt format | < 5ms | 2ms | | vLLM Prefill & TTFT | Chunked prefill + PagedAttention (A10G/A100)| < 45ms | 38ms | | Total Pipeline TTFT| End-to-End Real-Time RAG | < 100ms | ~73ms |


Production Pitfalls & Architect's Checklist

Before pushing your streaming RAG architecture to production, run through this architectural verification checklist:

GPU Memory Tuning

  • [ ] Avoid CUDA Out-Of-Memory (OOM) Errors: Set gpu_memory_utilization conservatively (e.g., 0.85 to 0.90) to reserve unallocated VRAM for dynamic allocation during execution spikes.
  • [ ] Monitor KV Cache Usage: Set up Prometheus metrics scraping vLLM's vllm:num_requests_waiting and vllm:gpu_cache_usage_perc. If KV cache usage consistently exceeds 85%, scale out your Ray actors.

Network and I/O

  • [ ] Disable Connection Buffering: Ensure all reverse proxies (e.g., NGINX, AWS ALB) have HTTP response buffering disabled (proxy_buffering off;). Buffering breaks real-time token streaming and blocks SSE feeds.
  • [ ] Set Keep-Alive Parameters: Keep gRPC and HTTP/2 connections warm between Ray Serve and your Serverless Vector DB to avoid TLS renegotiation overhead on new requests.

High Availability

  • [ ] Configure Health Check Endpoints: Implement specialized liveness and readiness probes in Ray Serve that verify GPU availability, model weight readiness, and remote Vector DB connectivity.
  • [ ] Graceful Degradation Strategy: If your vector search latency spikes beyond a set timeout (e.g., >60ms), fall back to an internal cache or serve a response generated without context to preserve system responsiveness.

Summary

Sub-100ms RAG is no longer theoretical—it is an achievable production standard. By replacing heavy, synchronous orchestration layers with Ray Serve, bypassing traditional memory layout limits with vLLM PagedAttention, and connecting to high-speed Serverless Vector DBs over persistent channels on AWS, you can deliver fast, highly scalable enterprise AI applications.


Have questions about tuning vLLM parameters, configuring Karpenter NodePools, or optimizing Ray clusters on AWS? Drop a comment below or reach out to the Ecstaticloud architecture team.