Jan 26 2026
services:
traefik:
image: traefik:v3.6
container_name: traefik
command:
# Enable Docker provider
- --providers.docker=true
- --providers.docker.exposedbydefault=false
# Entry point (HTTP)
- --entrypoints.web.address=:80
# Dashboard (local dev only)
- --api.dashboard=true
# Useful while learning
- --log.level=INFO
- --accesslog=true
ports:
- "8080:80"
volumes:
# Traefik reads container labels from Docker
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`traefik.localhost`)
- traefik.http.routers.traefik.entrypoints=web
- traefik.http.routers.traefik.service=api@internal
catalog-api:
build: ./CatalogApi
container_name: catalog-api
environment:
- ASPNETCORE_URLS=http://0.0.0.0:8080
labels:
- traefik.enable=true
- traefik.http.routers.catalog.rule=Host(`catalog.localhost`)
- traefik.http.routers.catalog.entrypoints=web
- traefik.http.services.catalog.loadbalancer.server.port=8080
billing-api:
build: ./BillingApi
container_name: billing-api
environment:
- ASPNETCORE_URLS=http://0.0.0.0:8080
labels:
- traefik.enable=true
- traefik.http.routers.billing.rule=Host(`billing.localhost`)
- traefik.http.routers.billing.entrypoints=web
- traefik.http.services.billing.loadbalancer.server.port=8080
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
// When running behind Traefik (reverse proxy), your app receives requests
// with forwarded headers (X-Forwarded-For, X-Forwarded-Proto).
// This middleware makes ASP.NET Core understand the original scheme/host.
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
// For local Docker networks, you often need to clear these defaults.
// In production, you should lock it down using KnownNetworks/KnownProxies.
options.KnownIPNetworks.Clear();
options.KnownProxies.Clear();
});
var app = builder.Build();
app.UseForwardedHeaders();
app.MapGet("/", (HttpContext ctx) =>
{
// This response is intentionally "debuggy" so you can see what Traefik changes.
return Results.Ok(new
{
service = "Catalog API",
host = ctx.Request.Host.Value,
scheme = ctx.Request.Scheme,
path = ctx.Request.Path.Value,
remoteIp = ctx.Connection.RemoteIpAddress?.ToString()
});
});
app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
// A realistic endpoint example
app.MapGet("/api/products", () =>
{
var products = new[]
{
new { id = 1, name = "Keyboard", price = 89.99 },
new { id = 2, name = "Mouse", price = 39.99 }
};
return Results.Ok(products);
});
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.Run();
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .
EXPOSE 8080
ENTRYPOINT ["dotnet", "CatalogApi.dll"]
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
options.KnownIPNetworks.Clear();
options.KnownProxies.Clear();
});
builder.Services.AddOpenApi();
var app = builder.Build();
app.UseForwardedHeaders();
app.MapPost("/api/invoices/calculate", (InvoiceRequest request) =>
{
var subtotal = request.Items.Sum(i => i.UnitPrice * i.Quantity);
var tax = Math.Round(subtotal * request.TaxRate, 2);
return Results.Ok(new
{
subtotal,
tax,
total = subtotal + tax
});
});
app.MapGet("/health", () => Results.Ok("OK"));
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.Run();
public record InvoiceRequest(
decimal TaxRate,
List<InvoiceItem> Items);
public record InvoiceItem(
string Name,
decimal UnitPrice,
int Quantity);
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .
EXPOSE 8080
ENTRYPOINT ["dotnet", "BillingApi.dll"]
docker compose up --build
1. Design Patterns that Deliver
This isn’t just another design patterns book. Dive into real-world examples and practical solutions to real problems in real applications.Check out it here.
Go-to resource for understanding the core concepts of design patterns without the overwhelming complexity. In this concise and affordable ebook, I've distilled the essence of design patterns into an easy-to-digest format. It is a Beginner level. Check out it here.
Every Monday morning, I share 1 actionable tip on C#, .NET & Arcitecture topic, that you can use right away.
Join 18,000+ subscribers to improve your .NET Knowledge.
Subscribe to the TheCodeMan.net and be among the 18,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.