← ClaudeAtlas

dotnet-opslisted

Production .NET operations advisor. Always use this skill when deploying or operating ASP.NET Core applications, configuring Kestrel, IHostedService, BackgroundService, health checks, graceful shutdown, SIGTERM, structured logging, or IOptions validation. Use when the user asks "graceful shutdown in ASP.NET Core", "BackgroundService exception handling", "IOptions eager validation", "health checks in .NET", "structured logging with Serilog", "SIGTERM in .NET", or "run background work in .NET". Read this skill before advising on any .NET production or deployment topic.
AllThingsSmitty/agent-skills · ★ 1 · Code & Development · score 74
Install: claude install-skill AllThingsSmitty/agent-skills
# Production .NET Operations ASP.NET Core is production-ready out of the box — but several defaults need explicit configuration before a service is truly operational: graceful shutdown, meaningful health checks, structured logging, and validated configuration. These aren't nice-to-haves; they're the difference between a service that degrades gracefully and one that drops requests during deploys. ## Graceful shutdown .NET's generic host handles `SIGTERM` and `SIGINT` automatically — it triggers `IHostApplicationLifetime.ApplicationStopping` and waits for hosted services to stop. Configure the timeout: ```csharp // Program.cs builder.Services.Configure<HostOptions>(options => { options.ShutdownTimeout = TimeSpan.FromSeconds(30); }); ``` For Kestrel (HTTP), in-flight requests are drained during shutdown. To stop accepting new connections while draining: ```csharp var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>(); lifetime.ApplicationStopping.Register(() => { // Signal readiness probe to return 503 — stops new traffic from routing here _isShuttingDown = true; }); ``` For custom resources (message consumers, database connections), implement `IHostedService`: ```csharp public class MyConsumer : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await foreach (var message in _queue.ReadAsync(stoppingToken)) { await ProcessAsync(message, stoppingToken);