seven / writing / 2026 / valki-140k-users3 min read
§ 02 · 2026-02-14 · Field Notes · Systems

VALKI: two years, 140k users, and what I actually learned

A retrospective on building a cross-platform game companion app from scratch — the technical decisions, the fires, and the part where the server got compromised.

VALKI is a companion app for Valorant players. I co-founded it in 2024 and have been the technical lead since day one. As of now it has around 140,000 active users, which still feels a little unreal to type.

This is a write-up of what actually went into building it.

The stack and why

Flutter for the mobile app, Java for the backend, PostgreSQL and Redis for storage. Running on a Linux (Ubuntu) server with Nginx in front.

The Flutter decision was easy — we needed iOS and Android with consistent UI and performance, and I didn't want to maintain two native codebases. Flutter's rendering engine gives you pixel-level control and the performance is genuinely good. We've never had a UI complaint that was a Flutter limitation.

Java for the backend was less obvious. I chose it because I was familiar enough with the JVM ecosystem and Java handles concurrency well out of the box. In hindsight it was the right call — we've had moments of rapid user growth and the backend held up.

The Redis decision

Around the time we crossed 10,000 users, response times started degrading under load. The bottleneck was obvious: too many database reads for data that almost never changed (game metadata, agent stats, map info).

We introduced Redis as a caching layer. The pattern was simple — check cache first, fall through to Postgres on miss, write back to cache. Read pressure on the database dropped significantly. Peak latency came down.

It's not a complicated setup but it's effective. The lesson: you don't need a distributed caching system until you need one, and when you need one, Redis is usually the right first choice.

The server incident

At some point during the second half of 2025, we started seeing strange behavior on the server. Unknown processes appearing in the process list. An unexplained shutdown. The kind of thing that makes you go look at logs immediately.

I spent a few hours going through system logs and ACPI event logs. The shutdown turned out to be triggered by a power event, not an intrusion — but the unknown processes were a legitimate concern. I traced them back to a suspicious file that had somehow ended up on the server and cleaned it out.

Nothing was lost and no user data was compromised. But it was the first time I had to treat a production server as a potential crime scene, and it changed how I think about server hygiene. I hardened the setup afterward: stricter SSH access, better log monitoring, regular audits.

Nginx and load balancing

Nginx sits in front of everything. Originally it was just a reverse proxy, but as traffic grew we added rate limiting and better request routing.

The most useful thing Nginx gives you in this setup is the ability to change what's behind it without the client noticing. We've done two backend migrations without downtime because we could route traffic selectively during the cutover.

What I'd do differently

The thing I underestimated most was operational load. Writing the code is one part. Keeping the server healthy, monitoring for issues, responding when something breaks at an inconvenient time — that's the other part, and it doesn't shrink as the product grows.

If I were starting now I'd invest more in observability earlier. We added proper log monitoring and alerting later than we should have.

The product itself I'm proud of. Getting to 140k without paid acquisition is something.

Back to the blog