Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Sizing Red Hat Quay: Profiling components to find real limits

October 29, 2026
Bala Tripura Bodapati
Related topics:
ContainersPerformance and Scale EngineeringObservability
Related products:
Red Hat QuayRed Hat OpenShift Data FoundationRed Hat OpenShift

    Red Hat Quay is a container image registry that stores, distributes, and helps secure container images across on-premise, cloud, and hybrid environments. When a large enterprise customer needed to validate Quay's performance capabilities for their deployment, we turned this sizing request into a systematic method for profiling every component in the architecture. Here is how we uncovered hidden bottlenecks across storage, client runtimes, and Red Hat OpenShift node limits, and how you can benchmark your own Quay cluster.

    The request

    A large enterprise customer operating Red Hat Quay across 160 nodes spanning 2 edge clusters was experiencing pull failures under load. Their question was straightforward: How many concurrent pulls can Quay handle before it breaks?

    We wanted to go beyond answering for 1 customer. The goal was to produce general sizing recommendations for a standard Quay deployment—numbers any customer or technical account manager (TAM) could use as a baseline, with the understanding that real-world performance would differ based on their specific environment, deployment choices, and resource allocation.

    Why sizing Quay isn't a simple answer

    Quay isn't a monolithic application. A QuayRegistry deployment is a system of 10 configurable components, each of which can be operator-managed or externally provided. Five directly affect pull throughput:

    ComponentWhat it doesOptions
    quayApplication pods serving the registry APIReplicas, vCPU, memory, Gunicorn workers
    postgresMetadata storage (repositories, tags, users, and permissions)Operator-managed or external (Amazon Relational Database Service (RDS), standalone)
    objectstorageImage layer blobsNooBaa/Red Hat OpenShift Data Foundation, Amazon S3, Azure Blob, Google Cloud Storage, Ceph RADOS, and 6 others
    redisBuild logs, model cache, garbage collection (GC) lockingManaged or external
    routeExternal ingress / TLS terminationHAProxy, Envoy, custom Ingress

    The remaining 5 (clair, horizontalpodautoscaler, mirror, monitoring, and tls) handle scanning, autoscaling, mirroring, metrics, and certificates—each configurable but less relevant to raw pull performance.

    Any one of these components can cap the throughput of the entire system. A customer using AWS S3 directly will see different numbers from one using NooBaa on Red Hat OpenShift Data Foundation. A deployment with 2 Quay replicas behind HAProxy behaves differently from 10 replicas behind Envoy. There's no universal "maximum throughput"—only throughput for a given configuration.

    We chose a specific configuration, documented it precisely, and tested against it. The numbers we produced are tied to that configuration. Customers should use them as a starting point, not a guarantee.

    Our system under test

    ComponentConfiguration
    PlatformAWS self-managed OpenShift, OVN-Kubernetes software-defined networking (SDN)
    Nodes3 control plane + 3 worker, m5.4xlarge (upgraded to m5.8xlarge for 16 vCPU tests)
    NetworkUp to 10 Gbps, Elastic Block Store (EBS) up to 4,750 Mbps
    Quay pods2 replicas, scaled from 2 to 16 vCPU per pod, Gunicorn workers \= CPU x 4
    PostgreSQLExternal, gp3 50Gi, 3,000 input/output operations per second (IOPS) baseline, 125 MB/s throughput, 500m CPU / 2Gi memory
    Object storageNooBaa on Red Hat OpenShift Data Foundation, S3-compatible, endpoint replicas varied during testing
    RedisExternal deployment in quay-enterprise namespace
    HPADisabled (to control horizontal pod autoscaler (HPA) replica count explicitly)
    ClairDisabled (not relevant to pull performance)
    Load balancerOpenShift Router (HAProxy)
    Test images171 MB each, 100 layers

    We used quay-performance-scripts, an open source framework that deploys an orchestrator pod on Red Hat OpenShift. The orchestrator creates worker pods that generate load against Quay, replicating what a container runtime does internally: authenticate, fetch the image manifest, and then download each layer blob over HTTP—6 layers in parallel per image, matching Podman's default behavior.

    We monitored every component using Dittybopper with Grafana dashboards backed by Prometheus—tracking CPU, memory, network, and connection counts across Quay, PostgreSQL, NooBaa, Redis, and HAProxy during every test run.

    First iteration: Establishing baselines

    We ran pull tests across 4 Quay configurations:

    Quay vCPU/podMemoryWorkersConcurrencyImages/secContent MB/sTotal images
    28Gi865~0.1728.83100
    416Gi16300~0.2034.80300
    832Gi323,700~0.2237.623,700
    1664Gi649,000~0.2339.349,000

    The concurrency ceiling scaled with vCPU—higher configurations could handle more simultaneous pulls without errors. But the per-image throughput was suspiciously flat. Quadrupling server resources from 2 to 8 vCPU only improved throughput from 0.17 to 0.22 img/s. Something was capping performance, and it wasn't the Quay application.

    As shown in Figure 1, pod processing limits were quickly reached during initial concurrency tests on the 4 vCPU configuration.

    Quay pod CPU utilization rising from 100% to 400% saturation during concurrent pulls on a 4 vCPU configuration.
    Figure 1: Pod CPU utilization reaches the 400% ceiling for a 4 vCPU Red Hat Quay replica during concurrency baseline testing.

    Figure 2 illustrates the extended CPU profile when scaling the Quay pod allocation to 8 vCPU.

    Quay pod CPU utilization steadily increasing to a 700% peak on an 8 vCPU configuration before dropping to idle.
    Figure 2: Pod CPU utilization climbs to nearly 700% on an 8 vCPU Red Hat Quay replica, demonstrating sustained throughput under load.

    Investigating the test output, we found the test framework averaged failed pulls (which timed out at 300 seconds) with successful ones, dragging down the reported throughput. We fixed the metrics to compute durations from successful pulls only (PR #94), which gave us accurate numbers to work with in subsequent tests.

    Systematic bottleneck elimination

    The following bottlenecks reflect our specific test architecture. While exact thresholds vary by storage, network, and platform setup, these findings highlight important areas to investigate when profiling your own Quay environment.

    Bottleneck 1: NooBaa storage saturation

    The Grafana dashboards told the story. During test runs, NooBaa object storage endpoints were hitting CPU and connection limits well before Quay pods experienced stress. Quay would request a blob, and NooBaa was the one struggling to serve it.

    We ran a systematic sweep, varying the NooBaa endpoint count for each configuration:

    Quay vCPUNooBaa endpointsConcurrencyImages/secSuccess
    845000.22245/500
    865000.52500/500
    867500.49748/750

    Scaling NooBaa from 4 to 6 endpoints more than doubled throughput and recovered from 49% failure to 100% success. The load barely stressed the Quay pods—they were waiting on storage.

    Figure 3 demonstrates how 4 NooBaa storage endpoints experienced CPU saturation during a 500-concurrency pull test.

    NooBaa endpoint CPU spiking past 100% saturation while Quay application CPU maintains ample headroom.
    Figure 3. Dittybopper metrics reveal NooBaa storage endpoints hitting CPU saturation at 4 replicas while Red Hat Quay application pods remain unstressed.

    As shown in Figure 4, adding 2 additional NooBaa endpoints distributed the processing load and restored throughput.

    NooBaa endpoint CPU utilization remaining below 70% across 6 endpoints, providing headroom under 500 concurrent pulls.
    Figure 4: Scaling NooBaa to 6 endpoints resolves storage bottlenecks, maintaining CPU utilization below 70% and achieving 100% pull success.

    This was our first major lesson: you must size the storage back end proportionally with Quay. The application pods are only one part of the overall architecture.

    Bottleneck 2: The test client itself

    Once we properly scaled NooBaa, we expected 16 vCPU Quay to outperform 8 vCPU. Instead:

    Quay vCPUConcurrencyImages/secSuccess
    8 (6 NooBaa)7500.49748/750
    16 (8 NooBaa)3000.56300/300
    16 (8 NooBaa)4000.0745/400

    The 8 vCPU configuration handled 750 concurrent pulls successfully, but the 16 vCPU configuration—with double the server resources—collapsed at 400.

    A tcpdump analysis of the client pod revealed TCP zero-window (win 0) packets; at 16 vCPU, Quay was serving data faster than the test client could consume it. The client's receive buffer was full.

    The root cause: Python's Global Interpreter Lock (GIL). The test framework uses ThreadPoolExecutor for concurrency. Python's GIL allows only 1 thread to execute bytecode at a time. At 400+ threads, the overhead of acquiring and releasing the lock thousands of times per second overwhelmed the actual HTTP work.

    We confirmed this by splitting 400 concurrency across 4 pods (100 each). Throughput recovered to approximately 1.5 img/s—a 21x improvement over the single-pod result, with the same total concurrency hitting the same Quay deployment.

    This was a critical finding: Performance numbers are only as reliable as the test client's ability to generate realistic load.

    During these long-running high-concurrency tests (some lasting 44+ hours), we also discovered authentication tokens were expiring mid-test. The original code retried with the same expired token, turning a transient issue into permanent failure. We added token refresh logic (PR #93) that detects 401 responses and requests a fresh token without consuming a retry attempt.

    Bottleneck 3: Thread and process identifier (PID) exhaustion

    At 5,000 concurrency on a single pod, we hit the kubelet daemon's podPidsLimit of 16,384. We raised this to 65,536 using a KubeletConfig custom resource (CR) and set corresponding cgroup limits in the container. This is a platform configuration issue any team running high-concurrency workloads on Red Hat OpenShift should be aware of.

    Sizing recommendations

    After systematically eliminating bottlenecks at each layer, we ran a final iteration with proper NooBaa endpoint scaling and produced these recommendations for our specific configuration (2 Quay replicas, NooBaa storage, 171 MB/100-layer images):

    Quay vCPU/podRecommended maximum concurrencyNooBaa endpointsThroughput (img/s)Content MB/s
    22520.2237.62
    47520.3763.27
    875060.4983.79
    16300*80.5695.56

    *The 16 vCPU concurrency ceiling reflects a single test client limitation, not Quay capacity. With distributed clients, 16 vCPU handles higher concurrency.

    What we learned

    When providing sizing recommendations for a multicomponent system like Quay, you must profile and tune every component in the architecture—not just the application itself. Each bottleneck we removed revealed the next one: storage → client tooling → platform limits.

    The numbers we produced are specific to our deployment configuration and will differ for customers using different storage back ends (such as S3, NooBaa, or GCS), different database tiers, different node types, or different network topologies.

    There's no single "Quay supports X concurrent pulls" answer. But with a documented reference configuration, systematic testing, and proper observability across every component, it's possible to establish reliable baselines for a given environment and scale from there.

    The approach we used—isolate each component, profile under load, and eliminate bottlenecks one at a time—applies to any Quay deployment regardless of configuration.

    Red Hat doesn't currently publish official capacity planning guidance for Quay. We hope this work—and the improvements to the open source test framework—helps define these performance baselines and provides a framework for testing Quay in your own environment.

    Resources:

    • quay/quay-performance-scripts: Load testing framework
    • Red Hat Quay Operator Components: Managed versus unmanaged components
    • cloud-bulldozer/performance-dashboards: Grafana dashboards (Dittybopper)
    • cloud-bulldozer/orion: Performance regression detection

    Related Posts

    • Push images to Quay without a password

    • Gain visibility into Red Hat Quay with Splunk

    • How to use Red Hat Quay as a proxy cache

    • Access Quay on OpenShift with short-lived credentials

    • Enhance data security in OpenShift Data Foundation

    • Right-sizing recommendations with MCOA and Perses dashboards

    Recent Posts

    • Sizing Red Hat Quay: Profiling components to find real limits

    • How DRA can improve the performance of distributed LLM inference workloads with RDMA and SR-IOV

    • Deploying third-party applications to Red Hat OpenShift: Common pitfalls and how to avoid them

    • Smaller model, sharper insights: How Atos approached structured outputs

    • Apache Camel tool calling: LLM integrations with bounded loops

    What’s up next?

    Learning Path 5G rollout learning path feature image

    Accelerate 5G core standalone rollout: An end-to-end testing pipeline with Red Hat...

    Deploy a 5G core testing pipeline to create a continuous quality check for a...
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility
    Ask AI