This Spring Boot file upload performance test started with a simple question:
How far can a Spring Boot file service go under realistic mixed file traffic before latency becomes the problem?
Backend APIs often feel fast in development because we test them with tiny files, a handful of requests, and very polite traffic.
Production is less polite.
File upload systems deal with large payloads, concurrent downloads, metadata lookups, deletes, previews, and sometimes video delivery. That combination can expose bottlenecks that never show up in a normal local test.
So I ran a load test against a Spring Boot file upload service and pushed it gradually up to 7,500 requests per minute.
The short version:
The service stayed alive with zero HTTP failures, but latency started degrading hard after roughly 6,300 RPM. The main bottleneck was not Spring Boot itself. It was serving large MP4 files directly through the backend.
Spring Boot File Upload Test Setup
The benchmark used FiloraFS-Lite, a lightweight Spring Boot file service boilerplate.
The service includes the typical file system APIs you would expect:
- file upload
- file download
- metadata lookup
- file listing
- delete operations
The goal was not to prove that this exact setup is universally fast. Benchmarks are always infrastructure-dependent.
The goal was more practical:
- find where latency starts to degrade
- compare small file and large file behavior
- track throughput under increasing load
- observe CPU and memory pressure
- understand whether the app fails suddenly or degrades gradually
The load runner was written in TypeScript and ramped traffic from 500 RPM to 7,500 RPM.
Spring Boot Load Test Summary
Here are the headline numbers from the full test:
| Metric | Result |
|---|---|
| Total requests | 134,517 |
| Peak target load | 7,500 RPM |
| Peak P95 latency | 1,878 ms |
| HTTP failures | 0 |
| Average CPU usage | 35.4% |
| Main bottleneck | MP4/video downloads |
P95 latency stayed manageable at lower traffic levels, then rose quickly once the test moved into the upper RPM range.
The most encouraging result was stability.
The service completed more than 134,000 requests without HTTP-level failures. That matters because many systems collapse before you get useful latency data.
This one did not collapse. It slowed down.
That distinction is important.
Spring Boot Latency Results
At lower load levels, the backend behaved comfortably.
From 500 RPM to around 5,100 RPM, P95 latency mostly stayed below 500 ms.
After roughly 5,700 RPM, latency began climbing faster.
Once the test crossed around 6,300 RPM, P95 latency moved beyond 1 second, which is where the user experience would start feeling visibly slow for many workflows.
In this specific benchmark, I would treat 5,000 to 5,500 RPM as the practical operating range if the target is to keep P95 latency around 500 to 600 ms.
Could the system accept more traffic? Yes.
Would I call the higher range comfortable for production without changing the file delivery architecture? No.
Spring Boot Throughput Results
Throughput tracked the target RPM well until higher load levels, where the service started falling behind.
The backend handled:
- more than 134,000 total requests
- 200 concurrent workers
- target traffic up to 7,500 RPM
- mixed file workloads
Throughput followed the target load reasonably well until the upper stages of the ramp test, where the system started falling behind.
That is a useful signal. It means the application was not failing because controllers, routing, or metadata APIs were broken. It was reaching a physical limit around file delivery.
For a file service, that is exactly the kind of thing you want to discover before real users discover it for you.
File-Type Latency: MP4 Was the Real Bottleneck
The most interesting result was the file-type latency breakdown.
MP4 downloads had much higher P95 latency than PDF and PNG files.
| File type | P95 latency | Observation |
|---|---|---|
| 4 ms | Very fast | |
| PNG | 338 ms | Moderate |
| MP4 | 1,707 ms | Main bottleneck |
That result changes how you should interpret the benchmark.
If the app were slow across every endpoint and every file type, I would look first at application code, database queries, thread pools, or request handling.
But that was not the pattern.
Small files were fine. Metadata-style operations were fine. The real pain showed up when the service had to deliver large MP4 files.
That points to a different class of bottleneck:
- disk IO
- network throughput
- response streaming
- buffering behavior
- JVM memory pressure
- request thread occupancy
In other words, the backend was good at being an API. It was less ideal as a media delivery layer under heavy load.
CPU Usage During the Spring Boot Load Test
CPU usage increased with traffic, but it did not look like the primary limiting factor.
CPU usage increased as traffic increased, which is expected.
But the average CPU usage stayed around 35.4% during the test. That suggests the service was not purely compute-bound.
If CPU had been pinned near 100%, I would look first at expensive transformations, compression, serialization, hashing, or application-level processing.
Here, the more likely pressure was IO and streaming large responses.
That is common in file systems. The bottleneck often moves away from "Can my controller handle the request?" and toward "Should this application server be responsible for pushing large media bytes at all?"
Memory Usage During the Performance Test
Memory usage rose during heavier stages but stayed within a manageable range during this test.
Memory usage rose during heavier stages of the test and peaked around 126 MB.
That does not prove a memory leak.
It does show that large file delivery creates more pressure than simple metadata requests. Depending on how files are read, buffered, streamed, cached, or proxied, memory can become a bigger concern as concurrency rises.
For production workloads, I would test this again with:
- larger files
- longer-running traffic
- more concurrent downloads
- different JVM heap settings
- object storage or CDN-backed delivery
- request patterns that mimic real users more closely
A short ramp test is useful, but it is not the same thing as a full soak test.
What This Spring Boot Performance Test Shows
My main takeaway is not "Spring Boot is slow."
The better takeaway is:
Spring Boot handled the API workload well. The scaling concern was using the application server as the primary delivery path for large media files.
That is an architecture issue more than a framework issue.
Spring Boot is a strong fit for:
- authentication
- authorization
- metadata management
- file ownership rules
- upload orchestration
- audit logging
- business workflows
But for high-volume file downloads, especially videos, I would usually move delivery away from the application server.
Production Architecture Recommendations
For a production-grade version of this architecture, I would separate application logic from file delivery.
A more scalable design would usually look like this:
- Spring Boot handles auth, metadata, permissions, and business rules
- files live in S3-compatible object storage such as AWS S3 or Cloudflare R2
- downloads use signed URLs
- public or semi-public assets go through a CDN
- very large files support efficient streaming and range requests
- the backend avoids becoming the main bandwidth bottleneck
Depending on the use case, Nginx static serving or internal redirects can also be useful. But for cloud-native systems, object storage plus signed URLs is usually the cleaner path.
The key idea is simple:
Let Spring Boot decide who can access a file. Do not force Spring Boot to personally deliver every large byte forever.
Practical Lessons From the Test
A few lessons stood out from this benchmark.
First, zero failures does not mean the system is healthy at every load level. A service can keep returning 200 responses while the user experience gets worse.
Second, P95 latency is more useful than average latency for this kind of system. File workloads tend to have uneven response times, especially when file sizes vary.
Third, endpoint-level metrics are not enough. File-type metrics told the real story here.
Fourth, performance testing should influence architecture. The answer is not always "tune the JVM" or "add more instances." Sometimes the answer is "this server should not be doing this job."
Need Production-Grade File Infrastructure?
This benchmark was conducted on FiloraFS-Lite, a lightweight starter built for experimentation and rapid setup.
If you need JWT auth, S3 storage, secure file access, streaming, and a production-ready architecture, FiloraFS-Pro is the next step.
Built for real production workloads.
Final Thoughts
The backend survived the full ramp to 7,500 RPM without HTTP failures, which is a good stability signal.
But the test also showed where the architecture starts to bend: large media delivery.
That is the real value of performance testing. It is not just about proving the app can handle traffic. It is about finding the point where an architectural decision becomes a bottleneck.
For this system, the next step is clear: keep Spring Boot in charge of API logic, but move heavy file delivery to infrastructure designed for it.
If you are building a production Spring Boot file upload system, that separation will matter more and more as your file sizes and traffic grow.
Related Articles
Spring Boot File Upload (Production Guide): Secure, Scalable System Design
Building file upload is easy. Making it production-ready is not. Learn security, scalable storage, and architecture patterns for real workloads.
Local Storage vs S3 for Spring Boot File Uploads
This benchmark exposed local file serving limits. Learn when local storage stops making sense and when S3 becomes the better architecture choice.
S3 Pre-Signed URLs in Spring Boot for Secure File Access
If direct file serving became your bottleneck, pre-signed URLs are one of the cleanest ways to offload downloads from your backend.





Top comments (0)