Tutorial December 9, 2025 7 min read

Using Feature Flags with .NET Aspire

.NET Aspire makes building distributed applications easier. Flagit integrates seamlessly with Aspire's service discovery, health checks, and configuration system. Here's how to set it up.

What is .NET Aspire?

.NET Aspire is Microsoft's opinionated stack for building cloud-native applications. It provides:

  • Service discovery and orchestration
  • Standardized configuration patterns
  • Built-in observability (logging, metrics, tracing)
  • Health checks and resilience

Flagit fits naturally into this ecosystem, providing feature flag management as another infrastructure component.

Setting Up the AppHost

First, add Flagit as a container resource in your AppHost project:

// AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);

// Add Flagit as a container
var flagit = builder.AddContainer("flagit", "s4labs/flagit")
    .WithVolume("flagit-data", "/data")
    .WithHttpEndpoint(port: 8080, targetPort: 8080, name: "http");

// Your API service references Flagit
var api = builder.AddProject<Projects.Api>("api")
    .WithReference(flagit);

// Your web frontend also references Flagit
var web = builder.AddProject<Projects.Web>("web")
    .WithReference(flagit);

builder.Build().Run();

This configuration:

  • Runs Flagit as a container with persistent storage
  • Exposes it on port 8080
  • Makes it available to your services via service discovery

Configuring the SDK

In your service projects, install the Flagit SDK and configure it to use Aspire's service discovery:

dotnet add package Flagit.Sdk
// Api/Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add service defaults (includes service discovery)
builder.AddServiceDefaults();

// Configure Flagit to use service discovery
builder.Services.AddFlagit(options =>
{
    // Use the service name from AppHost
    options.Url = "http://flagit";
    options.Key = builder.Configuration["Flagit:SdkKey"];
});

var app = builder.Build();

// Use Flagit in your endpoints
app.MapGet("/checkout", async (IFlagitClient flags) =>
{
    if (flags.IsEnabled("new-checkout"))
    {
        return Results.Ok(new { flow = "new" });
    }
    return Results.Ok(new { flow = "legacy" });
});

app.Run();

The http://flagit URL uses Aspire's service discovery. At runtime, it resolves to the actual container endpoint.

Health Checks Integration

Aspire includes health checks by default. The Flagit SDK registers its own health check automatically:

// The SDK automatically registers a health check
// You can see it in the Aspire dashboard under Health Checks

// To customize:
builder.Services.AddFlagit(options =>
{
    options.Url = "http://flagit";
    options.Key = builder.Configuration["Flagit:SdkKey"];
    options.HealthCheckName = "flagit"; // Custom name
    options.HealthCheckTags = ["ready"]; // Custom tags
});

The health check verifies:

  • Connection to Flagit server
  • Valid SDK key
  • Successful flag synchronization

Configuration with User Secrets

Store your SDK key securely using .NET's user secrets during development:

# Set the secret
dotnet user-secrets set "Flagit:SdkKey" "fgt_srv_your_key_here"

# In production, use environment variables or Azure Key Vault
# FLAGIT__SDKKEY=fgt_srv_your_key_here

Using Feature Flags in Components

Inject the Flagit client into your services and components:

// In a Blazor component
@inject IFlagitClient Flags

@if (Flags.IsEnabled("new-dashboard"))
{
    <NewDashboard />
}
else
{
    <LegacyDashboard />
}

// In a minimal API endpoint
app.MapGet("/api/features", (IFlagitClient flags) =>
{
    return new
    {
        NewCheckout = flags.IsEnabled("new-checkout"),
        DarkMode = flags.IsEnabled("dark-mode"),
        BetaFeatures = flags.IsEnabled("beta-features", new { plan = "pro" })
    };
});

// In a controller
[ApiController]
public class OrdersController : ControllerBase
{
    private readonly IFlagitClient _flags;

    public OrdersController(IFlagitClient flags)
    {
        _flags = flags;
    }

    [HttpPost]
    public IActionResult CreateOrder(Order order)
    {
        if (_flags.IsEnabled("async-order-processing"))
        {
            return AcceptedAtAction(nameof(GetOrder), new { id = order.Id });
        }
        // Synchronous processing
        ProcessOrder(order);
        return Ok(order);
    }
}

Real-Time Updates in Aspire

The Flagit SDK connects via SSE for real-time updates. In an Aspire application, this means:

  • Each service instance maintains its own SSE connection
  • Flag changes propagate to all instances within milliseconds
  • No restart required when flags change

Scaling Consideration

If you have many service replicas, each opens an SSE connection. For very large deployments (100+ instances), consider using a shared caching layer or polling mode instead.

Testing with Aspire

Aspire's testing support makes it easy to test with feature flags:

// IntegrationTests/ApiTests.cs
public class ApiTests : IClassFixture<DistributedApplicationTestFixture>
{
    private readonly DistributedApplicationTestFixture _fixture;

    public ApiTests(DistributedApplicationTestFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public async Task NewCheckout_WhenEnabled_ReturnsNewFlow()
    {
        // Arrange - Enable flag in Flagit
        var flagitClient = _fixture.CreateHttpClient("flagit");
        await flagitClient.PostAsync("/api/flags/new-checkout/enable", null);

        // Act
        var apiClient = _fixture.CreateHttpClient("api");
        var response = await apiClient.GetAsync("/checkout");

        // Assert
        var content = await response.Content.ReadFromJsonAsync<CheckoutResponse>();
        Assert.Equal("new", content.Flow);
    }
}

Complete Example

Here's a complete AppHost configuration with Flagit:

// AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);

// Infrastructure
var flagit = builder.AddContainer("flagit", "s4labs/flagit")
    .WithVolume("flagit-data", "/data")
    .WithHttpEndpoint(port: 8080, targetPort: 8080);

var redis = builder.AddRedis("cache");
var postgres = builder.AddPostgres("db");

// Services
var api = builder.AddProject<Projects.Api>("api")
    .WithReference(flagit)
    .WithReference(redis)
    .WithReference(postgres);

var worker = builder.AddProject<Projects.Worker>("worker")
    .WithReference(flagit)
    .WithReference(redis)
    .WithReference(postgres);

var web = builder.AddProject<Projects.Web>("web")
    .WithReference(api);

builder.Build().Run();

With this setup, you get:

  • Flagit running alongside your other infrastructure
  • Automatic service discovery for all services
  • Health checks visible in the Aspire dashboard
  • Real-time flag updates across all services

Learn More

Check out our .NET SDK documentation for more details on configuration options and advanced usage.

.NET SDK Docs