“The first time I deployed a Spring Boot application to Kubernetes, the platform team didn’t ask for my JAR file. Instead, they asked me a dozen questions that I had never considered before.”
Introduction
If you’ve spent most of your career building Java applications, your deployment experience probably looked something like this.
- Build the JAR
- Copy it to a server
- Restart Tomcat (or run the JAR)
- Verify the application starts
- Go home
The infrastructure team took care of everything else.
Kubernetes changes this completely.
The platform is no longer simply hosting your application—it is continuously making decisions about where, when, and how your application should run.
Before your application can even be deployed, you’ll likely receive a questionnaire similar to this.
---------------------------------------------------------
Application Name :
Environment :
Business Criticality :
---------------------------------------------------------
Replicas :
CPU Request :
CPU Limit :
Memory Request :
Memory Limit :
Peak TPS :
Average TPS :
Concurrent Users :
Startup Time :
Liveness Endpoint :
Readiness Endpoint :
Persistent Storage :
Database Connections :
Kafka Topics :
External APIs :
Expected Availability :
---------------------------------------------------------
The first time I saw this questionnaire, I understood barely half of it.
If you’ve ever wondered what 500m CPU, 2Gi Memory, Replicas, or Requests vs Limits actually mean, this article is for you.
By the end of this chapter, you’ll be able to have a meaningful conversation with your platform engineering team.
Before We Deploy Anything…
Let’s understand how Kubernetes sees your application.
A Java developer sees:
PaymentService.jar
Kubernetes sees:
Deployment
↓
ReplicaSet
↓
Pods
↓
Containers
↓
Java Process
Your Spring Boot application is simply another Linux process running inside a container.
Everything Kubernetes manages is built around that simple fact.
The Basic Deployment Unit
Many developers believe a Pod is the unit they deploy.
It isn’t.
The real deployment hierarchy looks like this.
Deployment
│
▼
ReplicaSet
│
▼
Pod
│
▼
Container
│
▼
Spring Boot JVM
Let’s understand each layer.
Deployment
A Deployment describes the desired state.
For example:
“I always want three instances of Payment Service running.”
Payment Deployment
Desired Replicas = 3
The Deployment itself never runs your application.
Instead, it delegates that responsibility.
ReplicaSet
The ReplicaSet continuously compares:
Desired Pods
vs
Running Pods
Suppose one pod crashes.
Desired
Pod-1
Pod-2
Pod-3
Actual
Pod-1
Pod-3
ReplicaSet immediately notices.
Missing Pod
↓
Create New Pod
This is the first level of Kubernetes self-healing.
Pod
A Pod is the smallest deployable unit.
Think of it as a wrapper around one or more containers.
+----------------------------------+
Pod
------------------------------------
Spring Boot Container
(Optional Sidecar)
Volumes
Networking
------------------------------------
In future articles we’ll see why Service Mesh introduces another container inside every pod.
Container
Finally…
Inside the container…
runs our familiar Spring Boot application.
Container
↓
java -jar payment-service.jar
Nothing magical.
Just another Java process.
The Questions Every Platform Team Will Ask
Let’s go through them one by one.
Question 1
How Much CPU Does Your Application Need?
This confuses almost every Java developer.
Unlike virtual machines…
Kubernetes measures CPU using millicores.
1000m = 1 CPU Core
500m = Half Core
250m = Quarter Core
2000m = 2 CPU Cores
So if someone asks
“Can your service run with 500m CPU?”
they’re really asking
“Can your application run with half a CPU core guaranteed?”
CPU Request
CPU Request is the minimum CPU Kubernetes guarantees.
Example
resources:
requests:
cpu: 500m
Meaning
Guaranteed CPU
0.5 Core
Kubernetes will not place your pod on a node unless it can guarantee this much CPU.
CPU Limit
CPU Limit defines the maximum CPU your application may consume.
resources:
limits:
cpu: "2"
Meaning
Maximum
2 CPU Cores
Request vs Limit
Think of it like driving on a highway.
Request
Reserved Lane
Limit
Maximum Speed
Your application may accelerate…
But never beyond the configured limit.
What Happens When CPU Limit is Reached?
Imagine your application suddenly receives heavy traffic.
Current Usage
2.4 CPU
Limit
2 CPU
Kubernetes does not kill the container.
Instead
CPU Throttling
The JVM becomes slower.
Response times increase.
Requests begin queueing.
This is why proper CPU sizing is critical.
Question 2
How Much Memory Does Your Application Need?
Memory works differently.
Unlike CPU…
Memory cannot be throttled safely.
Example
requests:
memory: 1Gi
limits:
memory: 2Gi
Meaning
Guaranteed
1 GB
Maximum
2 GB
What Happens if Memory Crosses the Limit?
This is one of the most common production issues.
Heap Growing
↓
Memory Limit
↓
Container Exceeds Limit
↓
OOMKilled
↓
Pod Restart
Unlike CPU…
Memory violations usually terminate your container.
JVM Heap vs Container Memory
One of the biggest mistakes Java developers make.
Container Memory
2GB
Java Configuration
-Xmx2G
Looks correct?
Actually…
No.
The JVM requires memory for much more than the Java heap.
Container Memory
--------------------------------
Java Heap
Metaspace
Thread Stacks
Native Memory
Direct Buffers
GC
JNI
Operating System
--------------------------------
If you allocate the entire container to the heap…
The operating system has nowhere else to allocate memory.
Result
OOMKilled
A good rule of thumb is to leave approximately 20–30% of the container memory for non-heap usage, then validate the setting under realistic load. The exact percentage depends on thread counts, libraries, and native memory usage.
Question 3
How Many Pods?
This is another common question.
Replicas
3
Does this mean the application is three times faster?
Not necessarily.
It simply means
Three Independent Copies
Running Simultaneously
How Traffic Flows
Service
│
----------------------------
│ │ │
Pod-1 Pod-2 Pod-3
The Service distributes traffic across available pods.
If Pod-2 crashes…
Service
│
Pod-1
Pod-3
↓
Traffic Continues
↓
New Pod Created
Users may never notice the failure.
Question 4
Expected TPS
Platform engineers often ask
What is your expected TPS?
TPS means
Transactions Per Second
Example
Average
120 TPS
Peak
850 TPS
This information helps determine
- Number of Pods
- CPU
- Memory
- Autoscaling Rules
TPS vs Concurrent Users
These are NOT the same.
Example
1000 Users
may generate
50 TPS
Another application with
50 Users
might generate
800 TPS
Always clarify which metric is being discussed.
Question 5
Startup Time
Spring Boot applications don’t start instantly.
Suppose
Startup Time
45 Seconds
But Kubernetes expects
5 Seconds
Result
Application Still Starting
↓
Health Probe Fails
↓
Container Restarted
↓
CrashLoopBackOff
This is why startup probes become extremely important.
We’ll dedicate an entire article to this topic later.
Question 6
Database Connections
Consider this configuration.
10 Pods
↓
50 Connections Per Pod
↓
500 Oracle Connections
Can your Oracle database support 500 simultaneous sessions?
Sometimes the bottleneck isn’t Kubernetes.
It’s the database.
Infrastructure sizing must consider the entire system.
Question 7
Storage Requirements
The platform team may ask:
Do you require persistent storage?
Not every application does.
Typical options include:
Temporary
↓
emptyDir
Persistent
↓
Persistent Volume
External
↓
Oracle
Redis
Amazon S3
Object Storage
For most stateless Spring Boot microservices, business data should reside in external systems rather than inside the container.
Question 8
External Dependencies
Your platform team also needs to understand what your application depends on.
Example
Oracle Database
Redis
Kafka
Solace
SMTP
REST APIs
LDAP
Vault
Every dependency affects startup, health checks, resilience, and deployment planning.
Horizontal Scaling
The cloud-native approach is to scale out.
1 Pod
↓
2 Pods
↓
5 Pods
↓
10 Pods
Instead of building one extremely large server.
Vertical Scaling
Traditional applications usually scale up.
2 CPU
↓
4 CPU
↓
8 CPU
Both strategies have their place.
Modern microservices generally prefer horizontal scaling because it improves availability and fault tolerance.
Sample Deployment Configuration
A typical deployment request might look like this.
Application : payment-service
Environment : SIT
Replicas : 3
CPU Request : 500m
CPU Limit : 2
Memory Request : 1Gi
Memory Limit : 2Gi
Peak TPS : 1200
Average TPS : 250
Concurrent Users : 1500
Startup Time : 40 Seconds
Database : Oracle
Redis : Yes
Kafka : Yes
Persistent Storage : No
Liveness :
/actuator/health/liveness
Readiness :
/actuator/health/readiness
Startup :
/actuator/health/startup
This isn’t just paperwork.
These values directly influence how Kubernetes schedules, scales, and protects your application.
A Practical Sizing Guide
The following table provides reasonable starting points. These values should always be validated through performance and load testing.
| Service Type | CPU Request | CPU Limit | Memory Request | Memory Limit | Replicas |
|---|---|---|---|---|---|
| Lightweight REST API | 250m | 1 Core | 512Mi | 1Gi | 2–3 |
| CRUD Service | 500m | 2 Cores | 1Gi | 2Gi | 2–4 |
| Payment Processing | 1 Core | 4 Cores | 2Gi | 4Gi | 3–6 |
| Event Consumer | 500m | 2 Cores | 1Gi | 2Gi | Based on partition count |
| Batch Worker | 2 Cores | 4 Cores | 4Gi | 8Gi | 1–2 |
Remember that these are reference values, not universal standards.
Production Tips
✔ Always define CPU and memory requests.
✔ Never set -Xmx equal to the container memory limit.
✔ Load test before deciding replica counts.
✔ Size database connection pools based on total pod count.
✔ Keep pods stateless.
✔ Define startup, liveness, and readiness endpoints before deployment.
✔ Monitor CPU throttling and OOMKilled events after go-live.
Common Mistakes
❌ Assuming one CPU equals one Java thread.
❌ Ignoring non-heap memory.
❌ Using local disk for business data.
❌ Guessing replica counts without load testing.
❌ Configuring unlimited CPU and memory.
❌ Forgetting that every additional pod increases database connections.
❌ Treating CPU requests and CPU limits as the same thing.
Infrastructure Checklist Before Deployment
Before handing your application to the platform team, make sure you can answer the following questions:
- What is the expected average and peak TPS?
- How many concurrent users are expected?
- How much CPU does the application require under peak load?
- What is the minimum and maximum memory requirement?
- How long does the application take to start?
- Does the application support horizontal scaling?
- What health endpoints are available?
- How many database connections will each pod open?
- Does the application write to local storage?
- What external systems must be available for startup?
If you cannot answer these questions, you’re not quite ready for production deployment.
Special Considerations for File Upload and Download Services
Most examples in Kubernetes documentation revolve around lightweight REST APIs that process JSON payloads. However, enterprise applications frequently expose services for uploading documents, downloading reports, processing images, generating PDFs, or exchanging files with object storage.
These workloads have very different infrastructure requirements.
If your application handles file uploads or downloads, your platform team will ask additional questions beyond CPU and memory.
Let’s understand why.
A Typical Enterprise File Upload Flow
Consider a banking application where customers upload KYC documents.
A request might follow this path:
Customer
│
▼
API Gateway
│
▼
Spring Boot Upload Service
│
├────────► Virus Scan
│
├────────► Object Storage (S3 / MinIO)
│
├────────► Oracle Metadata
│
└────────► Event (Kafka / Solace)
│
▼
Downstream Processing
Unlike a simple REST API, the application now performs several expensive operations within a single request.
- Receives a multipart request
- Validates the file
- Streams data
- Stores metadata
- Uploads to object storage
- Publishes an event
- Returns a response
Each step consumes CPU, memory, disk, and network bandwidth.
Questions the Platform Team Will Ask
For file handling applications, expect additional infrastructure questions such as:
Maximum Upload Size?
Average File Size?
Peak File Size?
Uploads Per Minute?
Downloads Per Minute?
Supported File Types?
Expected Concurrent Uploads?
Object Storage?
Temporary Storage Required?
Virus Scanning?
Encryption Required?
Compression Required?
These answers directly affect infrastructure sizing.
File Size Matters
Uploading a 20 KB JSON payload is very different from uploading a 500 MB video.
Consider the following examples.
| Workload | Average Payload |
|---|---|
| Customer Profile API | 5 KB |
| Payment API | 2 KB |
| PDF Upload | 8 MB |
| Passport Scan | 15 MB |
| High Resolution Image | 40 MB |
| Video Upload | 500 MB |
The infrastructure requirements increase significantly as file size grows.
Streaming vs Loading into Memory
One of the biggest design mistakes is reading the entire file into memory.
Avoid this approach:
byte[] bytes = multipartFile.getBytes();
For large files, every concurrent request allocates another large byte array on the JVM heap.
Instead, stream the file directly to storage whenever possible.
Client
↓
Input Stream
↓
Object Storage
↓
Close Stream
Streaming keeps memory usage predictable regardless of file size.
Memory Planning
Suppose your application allows:
- Maximum file size: 100 MB
- Concurrent uploads: 20
If every upload is fully loaded into memory, the application could theoretically require:
100 MB × 20
=
2 GB
And that’s before accounting for:
- JVM Heap
- Thread Stacks
- HTTP Buffers
- Spring Objects
- Metadata
- Native Memory
This is why file upload services generally require larger memory limits than standard REST APIs.
CPU Considerations
Uploading a file is not always CPU intensive.
However, CPU usage increases significantly if the application performs:
- Image resizing
- Thumbnail generation
- PDF generation
- OCR
- Compression
- Encryption
- Digital signing
- Checksum calculation
- Virus scanning
For these workloads, CPU sizing should be based on actual processing time rather than request count.
Network Bandwidth
Traditional REST services exchange small JSON payloads.
File services primarily consume network bandwidth.
For example:
100 Users
↓
Uploading
50 MB Files
↓
5 GB Network Traffic
Platform teams may therefore ask about:
- Average upload bandwidth
- Peak upload bandwidth
- Average download bandwidth
- Network throughput
- Internal storage traffic
This information is essential when multiple services share the same Kubernetes cluster.
Temporary Storage
Many upload services require temporary working space.
Examples include:
- Virus scanning
- ZIP extraction
- Image conversion
- Video transcoding
- PDF processing
In Kubernetes, temporary files are typically stored in an emptyDir volume.
Container
↓
emptyDir
↓
Temporary Files
↓
Deleted When Pod Terminates
Business data should never rely on this storage because it disappears when the pod is recreated.
Persistent Storage
Ask yourself one important question.
Should the application store the file locally?
In most cloud-native architectures, the answer is No.
A better approach is:
Client
↓
Spring Boot
↓
Object Storage
↓
Save Metadata
↓
Publish Event
The application stores only metadata in the database while the actual file resides in object storage such as Amazon S3, Azure Blob Storage, Google Cloud Storage, MinIO, or an enterprise object storage platform.
Large File Downloads
Download services have their own challenges.
Avoid loading the complete file into memory before sending it to the client.
Instead, stream the response.
Object Storage
↓
Input Stream
↓
Spring Boot
↓
HTTP Response Stream
↓
Client
Streaming reduces memory usage and improves scalability.
Timeouts
Large uploads take longer.
Infrastructure teams need to know:
- Expected upload duration
- Client timeout
- API Gateway timeout
- Load Balancer timeout
- Reverse Proxy timeout
If any component times out before the upload completes, the request fails even if the application is functioning correctly.
Infrastructure Sizing Example
Suppose you’re deploying a document management service.
A sizing request might look like this:
Application : document-service
Replicas : 4
CPU Request : 1 Core
CPU Limit : 4 Cores
Memory Request : 2Gi
Memory Limit : 4Gi
Maximum Upload Size : 100 MB
Average File Size : 8 MB
Concurrent Uploads : 50
Concurrent Downloads : 100
Temporary Storage : 5Gi emptyDir
Object Storage : Amazon S3
Virus Scan : Yes
Compression : No
Image Processing : Yes
Peak Upload Rate : 500 Files/Minute
Peak Download Rate : 1200 Files/Minute
Ingress Timeout : 10 Minutes
Average Response Time : < 3 Seconds
Health Endpoints :
/actuator/health/liveness
/actuator/health/readiness
/actuator/health/startup
This level of information enables the platform team to size worker nodes, configure ingress timeouts, allocate storage, and provision networking correctly.
Production Recommendations
For enterprise file services, consider the following best practices.
✔ Stream files instead of loading them into memory.
✔ Store files in object storage rather than inside containers.
✔ Keep only metadata in the database.
✔ Define maximum upload limits.
✔ Configure ingress and gateway timeouts for large uploads.
✔ Use asynchronous processing for OCR, thumbnail generation, virus scanning, and other long-running operations.
✔ Publish events after successful uploads to decouple downstream processing.
✔ Monitor upload latency, download throughput, network utilization, and object storage performance.
Final Thoughts
A file upload service is fundamentally different from a typical REST microservice.
Its success depends not only on CPU and memory but also on storage architecture, network throughput, streaming design, object storage integration, and asynchronous processing.
When discussing deployment with your platform team, be prepared to answer questions about file sizes, upload rates, storage strategy, bandwidth, and timeout configuration—not just replicas and CPU limits.
Understanding these characteristics will help you build scalable, resilient, and cloud-native file processing applications that perform reliably in production.
Key Takeaways
Kubernetes doesn’t just run applications—it manages resources.
As Java developers, we need to move beyond thinking only about code and begin thinking about capacity, scheduling, scalability, and resilience.
Understanding CPU requests, memory limits, replica sizing, startup characteristics, and infrastructure planning is just as important as writing clean business logic.
The better you understand these concepts, the more effectively you’ll collaborate with platform engineers and the more reliable your applications will become.
What’s Next?
So far we’ve learned how Kubernetes schedules and sizes workloads.
In the next article, we’ll look at the objects that make this possible.
We’ll explore Pods, ReplicaSets, Deployments, Namespaces, Labels, Selectors, and Services in detail, and see how they work together to create a highly available platform for Spring Boot microservices.