DEV Community

Cover image for High concurrency live Auction platform System Architecture
Mayuresh Smita Suresh
Mayuresh Smita Suresh Subscriber

Posted on

High concurrency live Auction platform System Architecture

The Problem: The "Cloud Bill" Trap

When building a live-streaming auction app like Whatnot, the default advice is always: "Just use AWS IVS or Mux."

It works great, until you do the math.

For a single 1-hour event with 10,000 viewers, AWS IVS (Interactive Video Service) charges based on "minutes watched."

  • 10,000 viewers × 60 minutes = 600,000 minutes
  • Cost per 1,000 minutes = ~$1.50
  • Total Cost = $900 (₹75,000 INR) per hour

If you run one event a day, your monthly bill is ₹22 Lakhs ($27,000). For a bootstrapped startup, this is a death sentence.

The Solution: The "Hybrid" Bare Metal Stack

We optimized this by moving away from "Pay-Per-Minute" (PaaS) to "Pay-Per-Bandwidth" (Infrastructure as a Service).

By switching to a self-hosted OvenMediaEngine (OME) instance on a dedicated server with unmetered bandwidth, we dropped the cost from $27,000/mo to $300/mo.

The High-Level Architecture

We use a Hybrid Approach:

  1. Video (Heavy Lifting): Hosted on a massive Bare Metal server (MilesWeb/Hetzner) using OvenMediaEngine
  2. Logic (The Brain): A lightweight Rust backend handling Auth, Payments, and the Auction Timer
  3. Real-Time (The Glue): WebSockets (Rust) for instant bidding, completely separate from the video stream
graph TD
    User[Mobile App User]
    Streamer[Live Seller]

    subgraph "Bare Metal Data Center"
      OME[OvenMediaEngine Video Server]
    end

    subgraph "Cloud API - DigitalOcean"
      Rust[Rust API & WebSocket Server]
      DB[(Postgres & Redis)]
    end

    Streamer -- "RTMP (OBS/Mobile)" --> OME
    OME -- "Webhook Validation" --> Rust
    Rust -- "200 OK" --> OME

    OME -- "LL-HLS (Video)" --> User
    User -- "WebSocket Bid (Data)" --> Rust
    Rust -- "Broadcast Winner" --> User
Enter fullscreen mode Exit fullscreen mode

Figure 1: Decoupled Video and Logic Architecture


Step 1: The Video Engine (OvenMediaEngine)

We chose OvenMediaEngine (OME) because it is open-source (C++) and supports Sub-Second Latency via LL-HLS (Low Latency HLS) and WebRTC.

Docker Configuration

We deploy OME using Docker on a MilesWeb Dedicated Server (32 Cores, 30TB Bandwidth).

# docker-compose.yml
version: "3.8"
services:
  ome:
    image: airensoft/ovenmediaengine:latest
    container_name: ome
    ports:
      - "1935:1935"    # RTMP Ingest
      - "8080:8080"    # LL-HLS Playback
      - "3333:3333"    # WebRTC Signaling
      - "3334:3334/udp" # WebRTC ICE
    environment:
      - OME_HOST_IP=${SERVER_PUBLIC_IP}
    volumes:
      - ./conf/Server.xml:/opt/ovenmediaengine/bin/origin_conf/Server.xml
    restart: always
Enter fullscreen mode Exit fullscreen mode

The "Secret Sauce" Config (Server.xml)

To get the latency down to 3 seconds (acceptable for auctions if the timer is synced separately), we tune the LL-HLS settings.

<Application>
    <n>auction</n>
    <Providers>
        <RTMP />
    </Providers>
    <Publishers>
        <LLHLS>
            <ChunkDuration>0.5</ChunkDuration>
            <SegmentDuration>6</SegmentDuration>
            <SegmentCount>3</SegmentCount>
            <CrossDomains>
                <Url>*</Url>
            </CrossDomains>
        </LLHLS>
        <WebRTC>
            <Timeout>30000</Timeout>
        </WebRTC>
    </Publishers>
</Application>
Enter fullscreen mode Exit fullscreen mode

Step 2: The Rust "Brain" (Stream Authentication)

You don't want just anyone streaming to your server. We use OME's Webhook feature to validate streamers against our database.

When a user starts streaming to rtmp://server/app/stream_key, OME hits our Rust API.

// src/routes/webhook.rs
use axum::{extract::Json, response::IntoResponse, http::StatusCode};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
pub struct OmeHook {
    name: String,      // This is the stream_key
    app_name: String,
}

#[derive(Serialize)]
pub struct OmeResponse {
    allowed: bool,
    reason: Option<String>,
}

pub async fn on_publish(Json(payload): Json<OmeHook>) -> impl IntoResponse {
    // 1. Verify Stream Key in Redis/Postgres
    // let is_valid = verify_stream_key(&payload.name).await;
    let is_valid = true; // Mock for demo

    if is_valid {
        println!("✅ Stream authorized: {}", payload.name);
        return (
            StatusCode::OK,
            Json(OmeResponse {
                allowed: true,
                reason: None
            })
        );
    }

    println!("❌ Stream rejected: {}", payload.name);
    (
        StatusCode::FORBIDDEN,
        Json(OmeResponse {
            allowed: false,
            reason: Some("Invalid Stream Key".to_string())
        })
    )
}
Enter fullscreen mode Exit fullscreen mode

Step 3: The Frontend (SolidStart Player)

On the client side (SolidStart), we use Hls.js optimized for low latency. We don't wait for the video to catch up; the Bidding Interface connects directly to the Rust WebSocket for real-time state.

// components/VideoPlayer.tsx
import { onMount, onCleanup } from "solid-js";
import Hls from "hls.js";

interface AuctionPlayerProps {
  streamUrl: string;
}

export default function AuctionPlayer(props: AuctionPlayerProps) {
  let videoRef: HTMLVideoElement | undefined;
  let hls: Hls | null = null;

  onMount(() => {
    if (Hls.isSupported() && videoRef) {
      hls = new Hls({
        lowLatencyMode: true,      // Critical for <3s delay
        backBufferLength: 90,
        liveSyncDurationCount: 3,  // Stay close to live edge
      });

      hls.loadSource(props.streamUrl);
      hls.attachMedia(videoRef);

      // Handle auto-play policies
      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        videoRef?.play().catch(e => console.log("Autoplay blocked", e));
      });
    }
  });

  onCleanup(() => {
    if (hls) hls.destroy();
  });

  return (
    <div class="relative w-full h-full bg-black">
      <video
        ref={videoRef}
        controls={false}
        muted={true} // Start muted to allow autoplay
        class="w-full h-full object-cover"
        poster="/images/loading-auction.jpg"
      />
      {/* Live Badge Overlay */}
      <div class="absolute top-4 left-4 bg-red-600 text-white px-2 py-1 text-xs font-bold rounded">
        🔴 LIVE
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The Result: Financial Breakdown

By moving the heavy video processing to a dedicated server and keeping the logic in efficient Rust microservices, we achieved massive savings.

Cost Component Cloud Native (AWS IVS) Our Hybrid Stack
Video Streaming ₹23,00,000 / mo ₹25,000 / mo (MilesWeb Dedicated)
API Servers ₹15,000 / mo ₹4,000 / mo (DigitalOcean)
Database ₹5,000 / mo ₹2,000 / mo (Supabase)
Latency ~3 Seconds ~3 Seconds
Total Monthly ~₹24 Lakhs ~₹31,000

Cost Savings: 99% Reduction 💰


Why This Works for Startups

Predictable Billing: We pay for the pipe (bandwidth), not the usage (minutes). A 30TB dedicated server costs the same whether you stream 100 hours or 1000 hours.

Rust Efficiency: Handling 10k WebSockets on a $10 server is trivial for Rust (Axum/Tokio), whereas Node.js would require a larger cluster.

Data Sovereignty: Hosting in India (MilesWeb) ensures compliance and faster speeds for local users without the AWS price tag.


Key Takeaways

  1. Separate Video from Logic: Video streaming is bandwidth-heavy but computationally light. Run it on bare metal with unmetered bandwidth
  2. Use Rust for Real-Time: WebSocket servers in Rust can handle 10x more connections per dollar than Node.js
  3. LL-HLS is Good Enough: For auctions, 3-second latency works if your bidding state is synchronized separately via WebSockets
  4. OvenMediaEngine is Production-Ready: We've been running OME for 6 months with zero downtime

Ready to Build Your Own?

Check out these resources:

Questions? Feel free to reach out to me on LinkedIn or check out what we're building at Ambicube Limited.


Written by Mayuresh Smita Suresh, Founder @ Ambicube Limited UK - I have vision for building the future of live commerce. Interested? Join me!

Top comments (0)