A modern Kubernetes Operator framework for .NET that makes it easy to build custom controllers and operators.
- π― Simple API - Fluent builder pattern for Kubernetes resources
- π Reconciliation - Built-in controller pattern with informers and caching
- π¦ Custom Resources - Full CRD support with type-safe definitions
- π³οΈ Leader Election - High availability out of the box
- π AOT Ready - Optimized for trimmed, single-file deployments
- π οΈ CLI Included - Built-in commands for install, create, and version
- π Source Generators - Automatic builder extensions for your resources
dotnet add package k8sOperator[KubernetesEntity(Group = KubeGroup, ApiVersion = KubeApiVersion, Kind = KubeKind, PluralName = KubePluralName)]
public class V1MyApp : IKubernetesObject<V1ObjectMeta>, ISpec<V1MyAppSpec>, IStatus<V1MyAppStatus>
{
public const string KubeApiVersion = "v1";
public const string KubeKind = "MyApp";
public const string KubeGroup = "modern-engineering.io";
public const string KubePluralName = "myapps";
public string ApiVersion { get; set; } = $"{KubeGroup}/{KubeApiVersion}";
public string Kind { get; set; } = KubeKind;
public V1ObjectMeta Metadata { get; set; } = new V1ObjectMeta();
public V1MyAppSpec Spec { get; set; } = new V1MyAppSpec();
public V1MyAppStatus Status { get; set; } = new V1MyAppStatus();
}
public class V1MyAppSpec
{
public int Replicas { get; set; }
public string? Image { get; set; }
}
public class V1MyAppStatus
{
public string? Phase { get; set; }
public int ReadyReplicas { get; set; }
}public class MyAppReconciler
{
public async Task<IReconcileResult> ReconcileAsync(ReconcileContext context)
{
var app = context.GetResource<V1MyApp>();
var informer = context.GetInformer<V1MyApp>();
// Your reconciliation logic here
// You can access other resources from the cache
var allApps = informer.List().Count();
return ReconcileResult.Success<V1MyApp>(x =>
{
x.WithLabel("managed-by", "simple-operator");
x.WithLabel("processed", "true");
x.WithStatus(x =>
{
x.Phase = "Reconciling";
x.ReadyReplicas = resource.Spec?.Replicas ?? 0;
});
});
}
}JsonSerializerContext is needed because it gives the .NET source generator a compile-time map of which types should be serialized and deserialized. That helps the framework avoid reflection-heavy runtime work and makes JSON handling more efficient, especially in trimmed or AOT-friendly applications.
[JsonSerializable(typeof(V1MyApp))]
[JsonSerializable(typeof(V1MyAppSpec))]
[JsonSerializable(typeof(V1MyAppStatus))]
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
GenerationMode = JsonSourceGenerationMode.Default)]
public partial class AppJsonSerializerContext : JsonSerializerContext
{
}var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOperator(x =>
{
x.Name = "simple-operator";
x.Namespace = "default";
x.JsonSerializerContext = AppJsonSerializerContext.Default;
x.Install.Resources.Add(typeof(V1MyApp));
});
var app = builder.Build();
app.AddReconciler<V1MyApp>(V1MyAppReconciler.ReconcileAsync);
await app.RunOperatorAsync();Use the fluent builder API:
var deployment = ObjectBuilder.Create<V1Deployment>()
.WithName("my-app")
.WithNamespace("default")
.WithLabel("app", "my-app")
.WithSpec(spec => spec
.WithReplicas(3)
.WithTemplate(template => template
.WithSpec(podSpec => podSpec
.WithContainer(c => c
.WithImage("nginx:latest")
.WithPort(80)))))
.Build();# Show version
myoperator version
# Install CRDs
myoperator install
# Create resource
myoperator create myapp --name demo
# Show help
myoperator helpConfigure via appsettings.json or attributes:
{
"Operator": {
"Name": "my-operator",
"Namespace": "default",
}
}- .NET 10.0 or later
- Kubernetes 1.25+
MIT License
Built with β€οΈ by Patrick Evers
