KubeJob.Client 1.0.0-beta

This is a prerelease version of KubeJob.Client.
dotnet add package KubeJob.Client --version 1.0.0-beta
                    
NuGet\Install-Package KubeJob.Client -Version 1.0.0-beta
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="KubeJob.Client" Version="1.0.0-beta" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KubeJob.Client" Version="1.0.0-beta" />
                    
Directory.Packages.props
<PackageReference Include="KubeJob.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add KubeJob.Client --version 1.0.0-beta
                    
#r "nuget: KubeJob.Client, 1.0.0-beta"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package KubeJob.Client@1.0.0-beta
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=KubeJob.Client&version=1.0.0-beta&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=KubeJob.Client&version=1.0.0-beta&prerelease
                    
Install as a Cake Tool

KubeJob

中文指南 · Getting Started · 本地开发环境 · Local Development · Architecture · Hardening Review

KubeJob is a typed, embeddable, distributed background-job runtime for .NET. It uses logical Runs, physical Attempts, pull-based workers, expiring leases, worker-session fencing, PostgreSQL transactions, an Outbox, and independent cron Schedule resources.

KubeJob provides at-least-once execution. It does not claim exactly-once external side effects.

Run locally

The repository includes a development stack for PostgreSQL and RabbitMQ. It automatically supports Docker Compose, podman compose, and podman-compose:

bash scripts/dev-stack.sh up

Run the unified sample against the real PostgreSQL store with one command:

bash scripts/run-unified-sample.sh

On Windows, use pwsh scripts/dev-stack.ps1 -Action up or pwsh scripts/run-unified-sample.ps1. The sample Dashboard is available at http://localhost:5041/admin/jobs; RabbitMQ management is available at http://localhost:15672. The included credentials are development-only.

Define a typed job

public sealed record SendEmail(string To, string Subject, string Body);

[KubeJob("mail.send")]
public sealed class SendEmailJob : IKubeJob<SendEmail>
{
    private readonly IEmailSender _sender;

    public SendEmailJob(IEmailSender sender) => _sender = sender;

    public ValueTask ExecuteAsync(
        SendEmail payload,
        JobExecutionContext context,
        CancellationToken cancellationToken) =>
        _sender.SendAsync(payload, cancellationToken);
}

The source generator creates a strongly typed key such as Jobs.SendEmail. JobExecutionContext is read-only and does not expose a service locator, repository, lease token, or fencing token.

Register and enqueue

builder.Services.AddKubeJobHandler<SendEmailJob, SendEmail>();

await jobs.EnqueueAsync(
    Jobs.SendEmail,
    new SendEmail("user@example.com", "Welcome", "Hello"),
    new JobEnqueueOptions
    {
        Queue = "mail",
        IdempotencyKey = "welcome:user-42",
        MaxAttempts = 5,
        Timeout = TimeSpan.FromMinutes(2)
    });

[KubeJob] declares only the stable handler key. Queue, priority, retry, timeout, idempotency, concurrency, scheduling, placement, batching, sharding, and broadcast behavior belong to submissions or dedicated resources.

Unified deployment

The control plane and worker can share one process without localhost HTTP:

builder.Services.AddKubeJobHandler<SendEmailJob, SendEmail>();
builder.Services.AddKubeJob(
    configureServer: server => server.UsePostgreSql(connectionString),
    configureWorker: worker =>
    {
        worker.WorkerId = Environment.MachineName;
        worker.MaxConcurrentJobs = 16;
        worker.Queues = new List<string> { "mail" };
        worker.BuildId = "mailer-2026.07";
    });

builder.Services.AddKubeJobDashboard(options =>
{
    options.RoutePrefix = "admin/jobs";
    options.AuthorizationPolicy = "KubeJobDashboard";
});

var app = builder.Build();
app.InitializeKubeJobDatabase();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

The host owns authentication and defines the named authorization policy. The in-process transport preserves the same Attempt, lease, retry, cancellation, and fencing semantics as distributed deployment.

Distributed deployment

Control plane:

builder.Services.AddKubeJobServer(options =>
    options.UsePostgreSql(connectionString));
builder.Services.AddKubeJobDashboard();

Worker:

builder.Services.AddKubeJobHandler<SendEmailJob, SendEmail>();
builder.Services.AddKubeJobWorker(options =>
{
    options.ServerEndpoint = "https://jobs.internal";
    options.WorkerId = Environment.MachineName;
    options.MaxConcurrentJobs = 32;
    options.Queues = new List<string> { "mail" };
    options.BuildId = "mailer-2026.07";
});

Workers request work only when they have free slots. PostgreSQL atomically creates an Attempt and lease with FOR UPDATE SKIP LOCKED. The server derives capacity from active Attempts and validates claims against the queues and capabilities registered by the Worker Session.

Independent schedules

await schedules.UpsertCronAsync(
    "daily-report",
    Jobs.GenerateReport,
    new GenerateReport("daily"),
    "0 2 * * *",
    new CronScheduleOptions
    {
        TimeZoneId = "Asia/Tokyo",
        Queue = "reports",
        MisfirePolicy = MisfirePolicy.FireOnce,
        ConcurrencyPolicy = ScheduleConcurrencyPolicy.SkipIfRunning
    });

Multiple control-plane replicas reconcile schedules through expiring claims and optimistic versions. Cursor advancement, Run creation, and Outbox creation occur in one PostgreSQL transaction.

Runtime model

JobSchedule ──creates──> JobRun ──contains──> JobAttempt

Worker ──starts──> WorkerSession ──temporarily owns──> JobAttempt

A retry or reassignment creates another Attempt under the same logical Run. Completion is accepted only from the current unexpired Attempt and active Worker Session. Stale workers cannot overwrite newer sessions.

Dashboard

AddKubeJobDashboard() provides V2-native operational pages:

  • runtime overview and Outbox backlog;
  • logical Run filtering and pagination;
  • Run detail with a complete Attempt timeline;
  • Worker Session state, epoch, capacity, queues, capabilities, labels, and heartbeat;
  • independent Schedule state, policies, and next/last fire time.

The Dashboard deliberately does not expose lease or fencing credentials. It is read-only by default, serialized job payloads are hidden by default, and its embedded UI has no public CDN dependency. Production hosts should bind it to their normal authorization policy:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("KubeJobDashboard", policy =>
        policy.RequireRole("KubeJobOperator"));
});

builder.Services.AddKubeJobDashboard(options =>
{
    options.RoutePrefix = "admin/jobs";
    options.AuthorizationPolicy = "KubeJobDashboard";
    options.ShowPayloads = false;
    options.AllowMutatingActions = false;
    options.MaximumWorkerSessions = 250;
    options.MaximumSchedules = 250;
});

Set ShowPayloads only when the route is protected and payload disclosure is acceptable. Set AllowMutatingActions to enable Run cancellation and Schedule enable/disable controls.

HTTP diagnostics

GET  /api/kubejob/jobs/{runId}
GET  /api/kubejob/jobs/{runId}/attempts
POST /api/kubejob/jobs/{runId}/cancel

PUT    /api/kubejob/schedules/{scheduleId}
GET    /api/kubejob/schedules/{scheduleId}
POST   /api/kubejob/schedules/{scheduleId}/enabled
DELETE /api/kubejob/schedules/{scheduleId}

Attempt history is the authoritative answer to which Worker Session executed a job; retries may move between nodes.

PostgreSQL schema

Kj2_JobRuns
Kj2_JobAttempts
Kj2_WorkerSessions
Kj2_JobSchedules
Kj2_Outbox

PostgreSQL is the source of truth. Optional MQ integration publishes only queue-specific wake-up hints from the transactional Outbox; duplicate or missing notifications cannot grant ownership.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-beta 83 7/26/2026