← Back to blog

June 14, 2026 • 3 min read

From Bash to CLI Part 6

The Numbers After All the Fixes

Baseline comparison across NestJS, Spring Boot, and Go at 1 CPU / 512MiB. Plus Express vs Fastify, virtual threads, and what surprised me.

TL;DR

Go dominates auth (6,804 req/s) with 58MiB memory. Spring Boot competitive on auth (3,226 req/s) but struggles on zones at 512MiB. Express vs Fastify is identical. Virtual threads don't help.

  • green-algeria-map
  • benchmark
  • go
  • spring-boot
  • nestjs

After fixing the six bugs, here’s what the Green Algeria Map benchmark actually measured.

Baseline: 3 Backends at 1 CPU / 512MiB

All scenarios at 0% failure. Three repeats per scenario, median reported. 90s holds per run.

Auth (20 VUs, 30s hold)

RankBackendavgp95req/sMem
1go2ms4ms6,80458MiB
2springboot4ms10ms3,226392MiB
3nestjs11ms36ms1,241390MiB

Zones (40 VUs, 30s hold)

RankBackendavgp95req/sMem
1go154ms366ms17458MiB
2nestjs288ms775ms94390MiB
3springboot497ms1,298ms54392MiB

Mix (30 VUs, 1m ramp + 30s hold)

RankBackendavgp95req/sMem
1go163ms391ms11258MiB
2nestjs302ms1,017ms60390MiB
3springboot503ms1,498ms37392MiB

Notes: All three backends peaked at ~100% CPU under load (1 CPU limit). Go sips memory at 58MiB while NestJS and Spring Boot settle at ~390MiB due to runtime overhead. Spring Boot’s zones/mix latency is impacted by thread-per-request model at 512MiB - with heap limited to 154MiB, the remaining memory goes to native thread stacks.

Express vs Fastify

Swapping @nestjs/platform-express for @nestjs/platform-fastify made no meaningful difference:

ScenarioExpressFastifyDelta
Auth avg10ms10ms+0ms
Zones avg319ms324ms+5ms
Mix avg157ms152ms-5ms
Auth req/s1,3301,354+2%
Zones req/s106104-2%
Mix req/s116127+9%

All within run-to-run noise. The bottleneck is application logic: DB queries, serialization, auth. Not the HTTP adapter. Fastify uses slightly less memory (~10-30MiB), but the difference is marginal.

Spring Boot Virtual Threads

spring.threads.virtual.enabled=true changed nothing. At 40 VUs with a 1 CPU limit, the bottleneck was CPU-bound request processing. Not thread stacks, not the DB pool.

Commit: dc0eaed2

What’s Planned

The current benchmark tests everything together: framework, DB, auth, serialization. It can’t isolate where the bottleneck actually is. The next phase will run isolated scenarios:

  • GET /ping: raw framework overhead
  • POST /echo: JSON serialization cost
  • POST /validate: validation cost
  • In-memory CRUD: business logic without DB
  • CRUD with PostgreSQL: DB layer isolation

Each layer measured separately to answer: is it the framework, the runtime, the ORM, the DB pool, the validation, or the auth?

Per-backend optimization posts will follow once the bottleneck is identified. The goal isn’t a winner. It’s understanding where each stack spends its time.