
Introduction
ArgoCD load testing is an important but often overlooked part of progressive delivery. Teams usually validate whether a rollout succeeds functionally, but they do not always verify whether performance remains stable while traffic shifts between versions. If you are using ArgoCD to manage Kubernetes deployments, blue/green releases, canary rollouts, or GitOps-driven application updates, load testing should be part of your release process.
Progressive delivery reduces deployment risk by exposing new versions gradually. However, that does not guarantee good performance. A new pod version may pass health checks and still introduce slower API responses, elevated error rates, CPU spikes, or degraded downstream dependencies under real traffic. This is where combining ArgoCD and LoadForge becomes powerful: ArgoCD automates delivery, while LoadForge validates application behavior under realistic load during each phase of rollout.
In this guide, you will learn how to use LoadForge for load testing and performance testing in ArgoCD-based environments. We will cover how ArgoCD-managed applications behave under load, how to write Locust scripts that target realistic application endpoints during rollouts, and how to analyze results to make better progressive delivery decisions. We will also look at authentication flows, version-aware routing, and rollout validation patterns that fit CI/CD and DevOps teams running Kubernetes in production.
Prerequisites
Before you begin load testing ArgoCD-managed applications, make sure you have the following:
- An ArgoCD instance managing one or more Kubernetes applications
- A target application deployed through ArgoCD, exposed via ingress, gateway, or load balancer
- Access to the application’s public or internal test URL
- A LoadForge account
- Basic understanding of:
- Kubernetes services and ingress
- ArgoCD applications, syncs, and rollouts
- Progressive delivery concepts such as canary or blue/green
- HTTP APIs and authentication
- Test credentials for your application, such as:
- API tokens
- Session-based login credentials
- OAuth or JWT-based auth if applicable
It is also helpful to have:
- A staging or pre-production environment that mirrors production
- Metrics from Prometheus, Grafana, Datadog, or similar observability tools
- CI/CD integration so LoadForge tests can run automatically during deployment stages
- If you use Argo Rollouts alongside ArgoCD, traffic-splitting rules configured through Istio, NGINX Ingress, Traefik, or a service mesh
While ArgoCD itself has API endpoints, most teams use load testing to validate the application being rolled out, not to stress the ArgoCD control plane. In this guide, we focus on testing the workloads managed by ArgoCD during progressive delivery.
Understanding ArgoCD Under Load
ArgoCD is a GitOps controller that continuously reconciles Kubernetes resources to match the desired state in Git. It is not usually the primary performance bottleneck in user-facing traffic. Instead, the real challenge is understanding how ArgoCD-managed applications behave while new versions are introduced.
What happens during progressive delivery
When ArgoCD syncs a new release, your Kubernetes cluster may:
- Launch new pods
- Terminate old pods
- Shift traffic gradually between versions
- Reinitialize caches
- Trigger database migrations
- Warm up JIT compilers, application runtimes, or connection pools
These events can create temporary performance instability even when deployment health appears green.
Common bottlenecks during ArgoCD rollouts
When load testing applications deployed with ArgoCD, watch for these common issues:
Cold start latency
New pods may take time to become truly performant after readiness passes. This is common with:
- Java and .NET services
- Services with large dependency graphs
- Applications that lazily initialize caches or models
Uneven traffic distribution
During canary releases, a small set of new pods may receive more load than expected due to sticky sessions, ingress behavior, or service mesh routing rules.
Database pressure
New application versions often introduce:
- More expensive queries
- Additional writes
- Different caching patterns
- Schema migration side effects
Load testing helps identify whether the new version increases database contention or latency.
Session and authentication issues
During rollout, you may see:
- Session invalidation between versions
- Token validation mismatches
- Inconsistent cookie behavior across old and new pods
Dependency saturation
Even if the application itself scales correctly, downstream services such as Redis, PostgreSQL, Elasticsearch, or third-party APIs may become the real bottleneck.
This is why performance testing for ArgoCD progressive delivery should simulate realistic user flows, not just hit a health endpoint.
Writing Your First Load Test
Let’s start with a basic Locust script that tests a typical web application deployed by ArgoCD. Imagine your application exposes:
GET /GET /healthzGET /api/v1/productsGET /api/v1/products/{id}
This is useful for validating baseline performance before and during a rollout.
from locust import HttpUser, task, between
class ArgoCDAppUser(HttpUser):
wait_time = between(1, 3)
@task(2)
def homepage(self):
self.client.get(
"/",
name="GET /"
)
@task(1)
def health_check(self):
self.client.get(
"/healthz",
name="GET /healthz"
)
@task(3)
def list_products(self):
self.client.get(
"/api/v1/products?category=devops&limit=20",
name="GET /api/v1/products"
)
@task(2)
def product_detail(self):
product_id = 101
self.client.get(
f"/api/v1/products/{product_id}",
name="GET /api/v1/products/:id"
)What this test does
This script simulates a simple traffic mix against an application managed by ArgoCD:
- Homepage requests validate public entry-point responsiveness
- Health checks can confirm whether service health endpoints remain stable
- Product listing requests simulate common read-heavy API usage
- Product detail requests test a more specific backend path
Why this matters for progressive delivery
Run this test:
- Before rollout to establish a baseline
- During rollout to compare canary vs stable behavior
- After rollout to confirm the final state performs as expected
In LoadForge, you can distribute this test across multiple cloud-based load generators and monitor real-time reporting while ArgoCD shifts traffic between versions. This makes it much easier to spot regressions that occur only during transition phases.
Advanced Load Testing Scenarios
Basic endpoint testing is useful, but realistic ArgoCD load testing should reflect actual user behavior. Below are more advanced scenarios that are especially valuable during progressive delivery.
Scenario 1: Authenticated API testing during rollout
Many ArgoCD-managed applications are internal dashboards, developer portals, or platform APIs that require authentication. This example simulates login plus authenticated API usage with JWT tokens.
Assume your application exposes:
POST /api/v1/auth/loginGET /api/v1/user/profileGET /api/v1/appsPOST /api/v1/apps/sync
from locust import HttpUser, task, between
import random
class AuthenticatedArgoCDAppUser(HttpUser):
wait_time = between(1, 2)
token = None
def on_start(self):
credentials = random.choice([
{"email": "platform-admin@example.com", "password": "ChangeMe123!"},
{"email": "devops-user@example.com", "password": "ChangeMe123!"},
{"email": "release-manager@example.com", "password": "ChangeMe123!"}
])
with self.client.post(
"/api/v1/auth/login",
json=credentials,
name="POST /api/v1/auth/login",
catch_response=True
) as response:
if response.status_code == 200:
body = response.json()
self.token = body.get("access_token")
if not self.token:
response.failure("No access_token returned")
else:
response.failure(f"Login failed: {response.status_code}")
def auth_headers(self):
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
@task(2)
def user_profile(self):
self.client.get(
"/api/v1/user/profile",
headers=self.auth_headers(),
name="GET /api/v1/user/profile"
)
@task(3)
def list_apps(self):
self.client.get(
"/api/v1/apps?project=platform&limit=25",
headers=self.auth_headers(),
name="GET /api/v1/apps"
)
@task(1)
def trigger_sync_preview(self):
payload = {
"appName": "checkout-service",
"dryRun": True,
"revision": "main",
"prune": False
}
self.client.post(
"/api/v1/apps/sync",
json=payload,
headers=self.auth_headers(),
name="POST /api/v1/apps/sync"
)Why this scenario is useful
Authentication paths often behave differently during deployments. Session stores, token validation libraries, and API gateways may be impacted by version skew. This test helps you validate:
- Login success rate during rollout
- Authenticated request latency
- Stability of user-specific endpoints
- Whether control actions such as sync previews remain responsive
This is especially important when ArgoCD deploys updates to applications used by platform engineering or release teams.
Scenario 2: Canary version validation with header-based routing
In many Kubernetes environments, canary releases are routed using ingress annotations, service mesh rules, or custom headers. A practical way to load test progressive delivery is to send traffic explicitly to stable and canary versions and compare the results.
Assume your ingress uses the X-Release-Track header to route traffic:
X-Release-Track: stableX-Release-Track: canary
And your application exposes:
GET /api/v1/ordersPOST /api/v1/cart/itemsPOST /api/v1/checkout
from locust import HttpUser, task, between
import random
class CanaryValidationUser(HttpUser):
wait_time = between(1, 3)
def release_headers(self):
track = random.choices(
["stable", "canary"],
weights=[80, 20],
k=1
)[0]
return {
"X-Release-Track": track,
"Content-Type": "application/json"
}, track
@task(4)
def browse_orders(self):
headers, track = self.release_headers()
self.client.get(
"/api/v1/orders?status=open&limit=10",
headers=headers,
name=f"{track} GET /api/v1/orders"
)
@task(3)
def add_cart_item(self):
headers, track = self.release_headers()
payload = {
"productId": random.choice([101, 205, 309, 412]),
"quantity": random.randint(1, 3),
"currency": "USD"
}
self.client.post(
"/api/v1/cart/items",
json=payload,
headers=headers,
name=f"{track} POST /api/v1/cart/items"
)
@task(1)
def checkout(self):
headers, track = self.release_headers()
payload = {
"customerId": f"cust-{random.randint(1000, 9999)}",
"paymentMethod": "card",
"shippingMethod": "standard",
"promoCode": "SPRING-DEPLOY"
}
self.client.post(
"/api/v1/checkout",
json=payload,
headers=headers,
name=f"{track} POST /api/v1/checkout"
)Why this scenario is useful
This script lets you compare stable and canary performance within the same test run. In LoadForge reports, you can quickly identify whether the canary version has:
- Higher latency
- More 5xx errors
- Increased failure rates on write-heavy operations
- Poorer performance on critical checkout or transaction paths
This is one of the most effective ways to do stress testing and performance testing during progressive delivery because it gives direct evidence about the new version before increasing traffic.
Scenario 3: Database-heavy rollout validation with custom response checks
Some of the biggest rollout risks appear in endpoints that hit the database heavily. During an ArgoCD deployment, a new version may introduce inefficient queries or increased write amplification. This example tests a reporting workflow with response validation.
Assume your application exposes:
GET /api/v1/reports/usageGET /api/v1/reports/deploymentsPOST /api/v1/search/events
from locust import HttpUser, task, between
from datetime import datetime, timedelta
import random
class ReportingUser(HttpUser):
wait_time = between(2, 5)
def date_range(self):
end = datetime.utcnow().date()
start = end - timedelta(days=7)
return str(start), str(end)
@task(3)
def usage_report(self):
start, end = self.date_range()
with self.client.get(
f"/api/v1/reports/usage?start={start}&end={end}&groupBy=service",
name="GET /api/v1/reports/usage",
catch_response=True
) as response:
if response.status_code != 200:
response.failure(f"Unexpected status code: {response.status_code}")
return
data = response.json()
if "results" not in data or not isinstance(data["results"], list):
response.failure("Missing or invalid results field")
@task(2)
def deployment_report(self):
start, end = self.date_range()
with self.client.get(
f"/api/v1/reports/deployments?start={start}&end={end}&env=prod",
name="GET /api/v1/reports/deployments",
catch_response=True
) as response:
if response.elapsed.total_seconds() > 3.0:
response.failure("Deployment report exceeded 3 seconds")
@task(1)
def search_events(self):
payload = {
"query": "rollout failed OR sync succeeded",
"filters": {
"cluster": random.choice(["prod-us-east-1", "prod-eu-west-1"]),
"namespace": "platform",
"severity": ["info", "warning", "error"]
},
"limit": 50,
"sort": {
"field": "timestamp",
"order": "desc"
}
}
with self.client.post(
"/api/v1/search/events",
json=payload,
name="POST /api/v1/search/events",
catch_response=True
) as response:
if response.status_code == 200:
body = response.json()
hits = body.get("hits", [])
if len(hits) > 50:
response.failure("Returned more hits than requested")
else:
response.failure(f"Search failed with {response.status_code}")Why this scenario is useful
This kind of test is ideal for identifying backend regressions introduced by new versions. It focuses on:
- Report generation latency
- Search performance under concurrent access
- Response correctness, not just status codes
- Database-heavy and aggregation-heavy operations
When run from LoadForge’s distributed infrastructure, this test can simulate realistic concurrency from multiple regions, which is useful if your ArgoCD-managed application serves globally distributed users.
Analyzing Your Results
After running your ArgoCD load testing scenarios, the next step is interpreting the results correctly. A rollout may appear healthy at the infrastructure level while still degrading user experience.
Key metrics to watch
Response time percentiles
Do not rely only on average latency. Focus on:
- P50 for typical user experience
- P95 for degraded but still common cases
- P99 for tail latency and rollout risk
If the canary version has significantly worse P95 or P99 latency than stable, that is a warning sign even if averages look acceptable.
Error rate
Track:
- HTTP 5xx responses
- Authentication failures
- Timeouts
- Custom validation failures from your Locust scripts
A small increase in errors during rollout may indicate issues with new pods, dependency startup, or version incompatibility.
Throughput
Compare requests per second before, during, and after rollout. If throughput drops while latency rises, the new version may be less efficient.
Endpoint-specific regressions
Break down results by endpoint name, especially if you used labels like:
stable GET /api/v1/orderscanary GET /api/v1/orders
This makes it easy to compare stable versus canary performance directly in LoadForge’s real-time reporting.
Correlate with Kubernetes and ArgoCD events
When analyzing performance testing results, correlate them with:
- Pod startup times
- Replica scaling events
- ArgoCD sync operations
- Rollout step transitions
- CPU and memory usage
- Database connection pool saturation
- Service mesh or ingress routing changes
Use LoadForge effectively
LoadForge helps here with:
- Distributed testing from global test locations
- Real-time dashboards during rollout
- Easy trend comparison across test runs
- CI/CD integration so tests can run automatically after ArgoCD syncs
- Cloud-based infrastructure so you do not need to manage load generators yourself
A strong practice is to run the same test profile at multiple rollout stages, such as 10%, 25%, 50%, and 100% traffic.
Performance Optimization Tips
Once your load testing reveals bottlenecks, here are some common optimization strategies for ArgoCD-managed applications.
Warm up new pods before shifting significant traffic
If your application has cold start issues:
- Preload caches
- Initialize DB connection pools at startup
- Run readiness checks that reflect true application readiness
- Delay traffic ramp-up until pods are actually performant
Tune Kubernetes resource requests and limits
If rollout causes CPU throttling or memory pressure:
- Increase CPU requests for more predictable scheduling
- Adjust memory limits to reduce OOM kills
- Review horizontal pod autoscaler behavior during deployment
Optimize database interactions
If database-heavy endpoints degrade under load:
- Add indexes for new query patterns
- Reduce N+1 queries
- Cache expensive report results
- Batch writes where possible
Validate ingress and service mesh routing
For canary or blue/green rollouts:
- Confirm traffic weights are actually respected
- Check for sticky session side effects
- Ensure header-based routing is consistent
- Monitor retries and timeouts at the proxy layer
Keep rollout steps aligned with performance thresholds
Do not shift traffic based only on health checks. Use performance testing thresholds such as:
- P95 latency under 500 ms
- Error rate below 1%
- No increase in timeout rate
- Stable throughput at target concurrency
Automate performance gates
A mature DevOps workflow uses LoadForge in CI/CD to automatically validate performance during ArgoCD-driven deployment pipelines. This makes progressive delivery safer and more repeatable.
Common Pitfalls to Avoid
ArgoCD load testing is highly effective, but there are several mistakes teams make repeatedly.
Testing only health endpoints
/healthz and /readyz are useful, but they do not represent real user experience. Always test meaningful business flows.
Ignoring rollout transitions
Many regressions happen during the transition itself, not after the rollout completes. Run tests while traffic is shifting.
Not separating stable and canary traffic
If you do not label or route requests clearly, you will struggle to tell which version caused the regression.
Using unrealistic traffic patterns
A constant stream of identical GET requests is rarely enough. Include:
- Auth flows
- Read/write mixes
- Search or reporting endpoints
- User think time
- Realistic payload sizes
Overlooking downstream dependencies
Your application may look fine, while PostgreSQL, Redis, or a third-party API becomes overloaded. Always correlate application results with dependency metrics.
Running tests from a single location only
If your users are geographically distributed, test from multiple regions. LoadForge’s global test locations make this much easier.
Forgetting response validation
A 200 OK does not always mean success. Validate response structure, timing, and business correctness in your Locust scripts.
Stress testing the ArgoCD control plane by accident
Unless your goal is explicitly to test ArgoCD itself, do not point heavy user traffic at ArgoCD APIs. The main objective is usually validating the deployed application under progressive delivery.
Conclusion
ArgoCD load testing is a critical part of modern progressive delivery. ArgoCD can safely automate Kubernetes rollouts, but only load testing and performance testing can tell you whether the new version actually performs well under real traffic. By combining ArgoCD with LoadForge, you can validate application behavior during canary releases, blue/green deployments, and full production rollouts with realistic Locust-based tests.
The examples in this guide showed how to test baseline endpoints, authenticated APIs, canary routing behavior, and database-heavy operations. With the right scripts, clear metrics, and rollout-aware analysis, you can catch performance regressions before they impact users.
If you want to make performance validation a standard part of your CI/CD and DevOps workflow, try LoadForge. Its distributed testing, real-time reporting, cloud-based infrastructure, global test locations, and CI/CD integration make it an excellent fit for ArgoCD-driven release pipelines.
LoadForge Team
LoadForge is a load and performance testing platform built on Locust. Our team has been shipping load tests against production systems since 2018, and we write these guides from real customer engagements.
Related guides
Keep going with more guides from the same category.

How to Automate Load Testing in CircleCI
Use LoadForge with CircleCI to automate load testing in CI/CD and detect bottlenecks before production.

Datadog Load Testing Integration with LoadForge
Integrate Datadog with LoadForge to correlate load test results with infrastructure and application metrics.

Jenkins Load Testing with LoadForge
Integrate LoadForge with Jenkins to automate load tests, fail slow builds, and improve release confidence.