LivePublicly verifiable

What a €40 server can do

Over 4 million companies and tens of millions of records. A single server. No microservices, no Kubernetes, no buzzwords.

Server8 vCPU, 16 GB RAM, €40/month
DatabasePostgreSQL 16 — 4M+ companies, tens of millions of records
CacheRedis 7 — TTL per data type
RuntimeBun + Fastify 5 — native TypeScript

Live test

100 real queries, 20 concurrent, against the production database

Press the button to run 100 real queries and measure server capacity.

Methodology

What we measure

The capacity test runs 100 real queries (company search, people search, CUI details, IBAN), 20 at a time, against the production database with over 4 million companies, representatives, and tens of millions of interconnected records. The concurrent user estimate assumes a real user makes on average 1 request every 3 seconds (page load + think time).

How we measure

Query functions are called directly on the server (no HTTP overhead), and time is measured with performance.now() — sub-millisecond resolution. Results include only server processing time, not network latency.

Formula

concurrent users = requests/second × 3s (avg time between actions)

Why it matters

A €40/month server with a well-indexed database and Bun as the runtime can serve thousands of concurrent users. No microservices, no Kubernetes, no million-dollar infrastructure needed.

What it costs elsewhere

The same server (8 vCPU, 16 GB RAM) at different providers — monthly prices, no discounts

DemoANAF.ro
40/mo
Hetzner Cloud
16/mo
OVHcloud
26/mo
Vultr
44/mo
DigitalOcean
58/mo
AWS EC2
180/mo
Google Cloud
150/mo
Azure
258/mo
Hetzner Cloud: shared CPUAWS EC2: on-demandGoogle Cloud: on-demandAzure: on-demand

Hyperscalers (AWS, Google, Azure) offer managed services, but for a portal with a well-indexed database, a simple VPS delivers the same performance at a fraction of the cost.

Test it yourself

Install k6 (open source, single binary) and run the script below.

import http from 'k6/http';
import { check, sleep } from 'k6';

// 50 utilizatori virtuali, 30 secunde
export const options = {
  stages: [
    { duration: '10s', target: 50 },
    { duration: '15s', target: 50 },
    { duration: '5s', target: 0 },
  ],
};

const BASE = 'https://demoanaf.ro';
const SEARCHES = ['construct', 'transport', 'consult', 'auto', 'euro'];
const PEOPLE = ['pop', 'ion', 'maria', 'gheorghe'];
const CUIS = [33503335, 4221306, 14399840, 18189442];

function pick(arr) { return arr[Math.floor(Math.random() * arr.length)]; }

export default function () {
  const roll = Math.random();
  let res;

  if (roll < 0.4)
    res = http.get(BASE + '/api/search?q=' + pick(SEARCHES) + '&limit=10');
  else if (roll < 0.65)
    res = http.get(BASE + '/api/company/' + pick(CUIS));
  else if (roll < 0.85)
    res = http.get(BASE + '/api/search/people?q=' + pick(PEOPLE) + '&limit=10');
  else
    res = http.get(BASE + '/api/iban?q=timis');

  check(res, {
    'status 200': (r) => r.status === 200,
    'response < 500ms': (r) => r.timings.duration < 500,
  });

  sleep(0.5);
}

Your results will include network latency — ours are measured directly on the server. The script uses 50 virtual users for 30 seconds, with a 0.5s pause between requests.