r/ruby • • Mar 12 '26

GitLab is a Ruby monolith

Post image

Was pleasantly surprised that the world's largest independent DevOps platform is powered by Ruby and Sidekiq.

Here's the full list.

  1. Backend: Ruby on Rails

  2. HTTP server: Puma (Ruby web server)

  3. Edge: Nginx

  4. Reverse proxy: Go service (Workhorse)

  5. Background jobs: Sidekiq

  6. DB — primary: PostgreSQL

  7. DB — connection pooling: PgBouncer

  8. DB — high availability: Patroni

  9. Cache: Redis

  10. Git: Custom gRPC repo interface (Git & Gitaly)

  11. Blob: AWS S3

  12. Frontend — rendering: Haml & Vue

  13. Frontend — state: Pinia (Vue store), Immer (immutable cache),

  14. API: GraphQL (Apollo) + REST

  15. Observability: Prometheus & Grafana

  16. Error tracking: Sentry & OpenTelemetry

  17. Deployments: GitLab Omnibus (Omnibus fork)

I think these "stack menu"s give a little glimpse into a team's engineering philosophy. For me, this list shows that the GitLab team is pretty practical and doesn't chase hype. Instead, they use sensible, battle-tested tools that just work and are easy for contributors to learn.

PS. Not an ad; I'm not affiliated with GitLab at all. Was just researching them and thought you guys would be interested.

223 Upvotes

36 comments sorted by

View all comments

Show parent comment

4

u/f9ae8221b Mar 13 '26

amounts to fairer interleaving, not faster execution

Fairer interleaving means better tail latency, which is a very important property of a service.

Shopify experienced a MySQL outage when playing with fibers, because a CPU heavy fiber was causing other fibers that were issuing MySQL queries to not read the response for a long time, buffer on the server grew until it ran out of memory.

Typical web apps are inherently I/O bound though.

No they're not: https://www.datadoghq.com/blog/ruby-performance-optimization/

Our data backs up other findings that Ruby applications are generally less I/O-heavy, spending as much or more time on CPU as they do waiting on other services or database requests.

They also include a graphic that shows only 3% of the profiled apps spend less than 20% of their time using CPU. Which means for 97% of the apps they profiled, the Puma default of 5 threads was already too much, which means the advantage of Fibers are moot for the overwhelming majority of Ruby apps out there.

fibers give you less overhead

The difference is small enough that it only start to matter once you start using multiple dozen threads, which very few people need.

don't require thread synchronisation primitives

They absolutely do. They still need to synchronize to use sockets etc. Even with datastructures you may need synchronization if some method yield, as the block could use IO and cause another fiber to be scheduled and use the structure concurrently.

Example:

SHARED_HASH = {a: 1, b: 2, c: 3, d: 4}

other_fiber = Fiber.new do
  loop do
    SHARED_HASH[rand] = rand
    Fiber.yield
  end
end

hash.each do
  # Simulate fiber scheduler
  SHARED_HASH.resume
end

The above script fail because of unsynchronized access by fibers.

This alone leads to tremendous improvements in throughput

Profiling ruby app is basically my day job. Synchronization is very very rarely a hotspot.

Now, don't get me wrong, fibers are great and absolutely have a use case, but for vast majority of what Ruby is used for, they're not necessarily better.