Ocelot API Gateway + .NET: production patterns that work
Sep 12, 2025•
ocelotapi-gateway.netmicroservices
• 0
Ocelot is a lightweight, code-first API Gateway for .NET. It handles routing, aggregation, auth, rate limiting, caching, and resiliency in front of your downstream services.
When to use Ocelot
- You’re on .NET and want a simple gateway without external infra
- You need service aggregation, token validation, and per-route policies
- You prefer configuration as code and easy containerization
If you need L7/WAF, global edge, or WebSockets at scale, consider Azure Front Door + YARP, NGINX, or Envoy. Ocelot is great for single-region app/API gateways.
Quick start
dotnet new web -n Gateway
cd Gateway
dotnet add package Ocelot
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot();
var app = builder.Build();
await app.UseOcelot();
app.Run();
ocelot.json
minimal example:
{
"Routes": [
{
"DownstreamPathTemplate": "/api/users/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [{ "Host": "users.api", "Port": 443 }],
"UpstreamPathTemplate": "/users/{everything}",
"UpstreamHttpMethod": ["GET", "POST", "PUT", "DELETE"]
}
],
"GlobalConfiguration": {}
}
Auth with JWT (Azure AD / Auth0 / Cognito)
Validate tokens at the gateway, forward user claims to services.
{
"Routes": [
{
"UpstreamPathTemplate": "/orders/{everything}",
"UpstreamHttpMethod": ["GET", "POST"],
"DownstreamPathTemplate": "/api/orders/{everything}",
"DownstreamHostAndPorts": [{ "Host": "orders", "Port": 8080 }],
"AuthenticationOptions": {
"AuthenticationProviderKey": "JwtScheme",
"AllowedScopes": ["orders.read", "orders.write"]
},
"AddHeadersToDownstream": {
"X-User-Id": "{Claims[oid]}"
}
}
]
}
Program.cs (auth setup):
builder.Services
.AddAuthentication()
.AddJwtBearer("JwtScheme", options =>
{
options.Authority = builder.Configuration["Auth:Authority"]; // e.g. https://login.microsoftonline.com/tenant
options.Audience = builder.Configuration["Auth:Audience"]; // api://gateway
});
Resilience & QoS
Enable retries, timeouts, circuit breakers per route.
{
"Routes": [
{
"UpstreamPathTemplate": "/inventory/{everything}",
"DownstreamPathTemplate": "/api/inventory/{everything}",
"DownstreamHostAndPorts": [{ "Host": "inventory", "Port": 8081 }],
"QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 5000, "TimeoutValue": 4000 },
"LoadBalancerOptions": { "Type": "RoundRobin" }
}
]
}
Aggregation (single call → multiple services)
Use Aggregates
to compose responses.
{
"Routes": [
{ "UpstreamPathTemplate": "/profile/{userId}", "DownstreamPathTemplate": "/api/users/{userId}", "Key": "user" },
{ "UpstreamPathTemplate": "/profile/{userId}/orders", "DownstreamPathTemplate": "/api/orders/by-user/{userId}", "Key": "orders" }
],
"Aggregates": [
{
"UpstreamPathTemplate": "/profile-aggregate/{userId}",
"RouteKeys": ["user", "orders"],
"Aggregator": "FakeDefinedAggregator" // or custom aggregator class
}
]
}
Custom aggregator example:
public class ProfileAggregator : IDefinedAggregator
{
public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
{
var user = await responses[0].Items.DownstreamResponse().Content.ReadAsStringAsync();
var orders = await responses[1].Items.DownstreamResponse().Content.ReadAsStringAsync();
var json = $"{{\"user\":{user},\"orders\":{orders}}}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
return new DownstreamResponse(content, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "OK");
}
}
Register it:
builder.Services.AddOcelot().AddSingletonDefinedAggregator<ProfileAggregator>();
Rate limiting & caching
{
"Routes": [
{
"UpstreamPathTemplate": "/public/{everything}",
"DownstreamPathTemplate": "/api/public/{everything}",
"RateLimitOptions": { "EnableRateLimiting": true, "Period": "1m", "PeriodTimespan": 60, "Limit": 120 },
"FileCacheOptions": { "TtlSeconds": 30, "Region": "public-cache" }
}
],
"GlobalConfiguration": { "RateLimitOptions": { "DisableRateLimitHeaders": false } }
}
``]
## Deployment notes
- Package as a container; mount `ocelot.*.json` via config map/volume for hot reloads.
- Use health checks that probe downstreams and expose `/health`.
- Keep gateway stateless; scale horizontally behind a load balancer.
## Checklist
- [ ] Per‑route auth scopes and headers
- [ ] Timeouts, retries, and circuit breakers
- [ ] Aggregation where it adds value only
- [ ] Global + per‑route rate limits
- [ ] Access logs and correlation IDs
- [ ] Config as code with environment overrides
With Ocelot, you can stand up a capable gateway in hours, keep policies versioned with code, and evolve routes safely as your .NET services grow.