Most ad-tech companies keep their architecture secret. We think being open builds more trust. If you give us your ad budget, you should know how the machine that spends it works. So here it is — shared the way the engineering blogs we admire do it, Netflix's above all. Plain English. No versions or configs (we explain why at the end).
Updated 13 October 2026: the context engine, which joined the system after this post was first published, is now in the stack list, the diagram and the walkthrough, and the channel connector is described as it works today.
The stack we run
Everything below is already named in our own job posts, so nothing here is a new secret. But seeing it in one place tells you how we build:
- The bidder is written in Go. Go is fast and predictable — and our whole budget for a decision is a few milliseconds.
- Machine learning is Python — XGBoost, LightGBM and PyTorch. We export the trained models to ONNX, an open format. That way a model trained in Python runs at full speed inside the bidder. No Python in the hot path.
- Dashboards are Laravel (PHP) APIs with a React front end. Boring, mature tools — on purpose. Campaigns, budgets and billing must never be wrong.
- Redis holds the campaign and feature cache. The bidder reads it on every request.
- Redpanda → ClickHouse is our data pipeline. Redpanda speaks the Kafka protocol; every auction event streams through it into ClickHouse, where billions of rows become reports in seconds.
- PostgreSQL is the system of record behind the dashboards. Campaigns, budgets and billing live here — the numbers that must never be wrong.
- A small Python service — our ad-channel connector — manages the campaigns a plan also runs on partner networks: video, search and social. Campaign changes go out; performance reports come back into ClickHouse, so every channel lands in the same analytics. It lives completely off the bidding path.
- A second Python service — our context engine — reads sites and apps before the auction. Each one is profiled once, into a short description and a verdict on what it is about, and the verdict is written into Redis so the bidder can look it up in a single cache read; the profiles land in ClickHouse. The bidder tells it which sites and apps it has been seeing, through Redpanda, and the dashboards brief it with what an advertiser is looking for. How we rebuilt it, and what it does for an advertiser.
- Everything runs on Kubernetes on AWS (EKS), spread across several data-centre zones. All infrastructure is written as code with Terraform — no hand-built servers.
The system, on one page
Follow one request through the picture. A bid request arrives — from an exchange, or from our ad SDK on a publisher's page. The load balancer spreads the traffic. A thin ingress layer ends the TLS connection and routes the request to a bidder pod. The bidder itself checks every request — is it valid, is it allowed — before doing any work. There is no heavy API-gateway product in front of it, on purpose: every extra hop on this path costs time we don't have. The bidder never touches a database during the auction. Campaign data comes from Redis — and so does the verdict on what the site or app is about, worked out earlier by the context engine, so understanding the content costs the auction one more cache read. The value of the impression comes from an ONNX model sitting right next to the bidding code. The bidder decides, answers, and moves on.
After the answer is sent, the full story of that auction flows into Redpanda, lands in ClickHouse, and shows up on a dashboard moments later. The dashboards read and write across the whole data layer — PostgreSQL for the records, ClickHouse for analytics, Redis and Redpanda for the live stuff. Training data flows back out to build the next model. The sites and apps the bidder has been seeing flow to the context engine, which profiles any it has not met before, ahead of the next request. The loop closes.
Nothing slow is allowed while the auction is running. Databases, model training, reports — all of that happens before or after, never during. During the auction there is only Go, one cache read, and one model inference. This one rule is most of why the numbers below are possible.
Security by design
Here is our security in plain terms. We describe layers and principles — not versions, addresses or rules. Publishing those would help attackers, so we don't.
- Encrypted everywhere. TLS on every outside connection, and between services too.
- Least privilege. Every service gets only the access it needs. No shared master keys.
- Sandboxed ads. Ads render inside sandboxed iframes, so an ad can't touch the page around it. You can see this in our public ad SDK.
- Consent comes first. The SDK reads the page's consent state (TCF, GPP, US privacy) and the server respects it. No consent means non-personalised ads.
- Infrastructure as code. Every firewall rule and permission lives in Terraform, so changes are reviewed like code. When the code and the running system do drift apart, we treat the difference as a bug — here is a time it happened.
- An open door for researchers. We publish a security.txt. If you find something, we want to hear about it.
How it scales
Ad traffic comes in waves, and a late answer is a lost auction. Here is how we handle that:
- Stateless bidders. Any pod can answer any request. Kubernetes adds pods when traffic rises and removes them when it falls.
- Cache first. The bidder reads from Redis and its own memory, not from a database. A traffic spike just means more cheap cache reads.
- Compute early. Anything that can be worked out before the auction — what a site or app is about, campaign eligibility, model files — is ready in advance and simply looked up.
- Everything else is async. Logging, billing and analytics happen after the answer is sent. A slow report can never slow an auction.
- Several zones at once. If one data-centre zone fails, we lose some capacity — not the service.
Speed without a big bill
Cloud bills kill ad-tech margins, so cost per request matters as much as speed. The same choices that make the system fast also make it cheap. Go uses little memory, so one server does more work. ONNX means no GPU fleet just to serve models. Caching keeps the databases small. Autoscaling means we pay for the traffic we get — not the traffic we might get someday. Today this architecture handles close to ten million ad requests a day — a few hundred every second, with peaks under 400 — on a footprint a seed-stage company can afford.
That figure comes from our own load tests, not audited production traffic. We say so on purpose — we would rather under-claim than over-claim. Serious partners are welcome to a private walkthrough of the details.
What we deliberately don't share
Openness has limits, and it's more honest to name them. Three things will never appear on this blog: how our fraud detection works (that would teach fraudsters how to hide), how our bid pricing works (that's the product), and software versions or configuration (that's a free map for attackers). Everything else — the shape of the system, the trade-offs, the lessons — we'll keep sharing.
Building things like this from Dhaka, for Asia? We're hiring (applications reopen soon). Curious what all this speed is for? Start with how ad auctions work or request a demo.