Deployment

Process shapes, containers, the migration step, shutdown timeouts, capabilities and a checklist for going live.

On this page

Runwick has no server of its own. It runs inside your processes, and the store (PostgreSQL, SQLite or Redis) is the only shared part. What a process does is decided by its configuration, not by which program it is.

Roles

RoleTurned on by
ProducerAlways: any process with AddRunwick can dispatch
WorkerAddWorker(...)
SchedulerAddScheduler()
Management hostMapRunwickManagement(...) and/or MapRunwickDashboard(...)

A process can have any combination.

Common shapes

One web app dispatches, runs workers, runs the scheduler and serves the dashboard. Simplest to run, and fine for many apps.

Text
builder.Services.AddRunwick(r =>
{
    r.Application("shop").UsePostgres(cs).AddJobsFromAssemblyContaining<Program>();
    r.AddHandlersFromAssemblyContaining<Program>();
    r.AddScheduler();
    r.AddWorker(w => w.Pool("main", p => p.Queues("default").FixedConcurrency(8)));
});

The trade-off: a slow job and a web request compete for the same CPU, and every deploy restarts both.

SQLite supports all of these only when every process is on the same machine. See Choosing a store.

Migrations are a separate step

Runwick does not migrate the store when your app starts. Run migrations as a deploy step, with credentials that may create schemas:

Text
runwick migrate --provider postgres --connection-env RUNWICK_MIGRATE_CS

or from code, for example in a one-off job:

Text
await RunwickPostgresMigrations.MigrateAsync(migrationConnectionString);

The running app can then use a role that only reads and writes data. migrate is safe to run again: a second run applies nothing.

A binary works with exactly one store revision. A process that meets a store at another revision refuses to run. So on an upgrade the migration step is part of a short stop of new work, not something you can roll out replica by replica. Upgrades has the procedure.

Containers and shutdown

The worker needs up to 25 seconds to stop cleanly (drain, then cancel, then write results). Give it room at every layer:

LayerDefaultSet it to
Runwick ShutdownBudget25 sLeave it, or change the slices in Workers
.NET HostOptions.ShutdownTimeout30 sAbove Runwick's budget
Container grace period (Kubernetes terminationGracePeriodSeconds)30 sAbove the host's timeout
Text
builder.Services.Configure<HostOptions>(o => o.ShutdownTimeout = TimeSpan.FromSeconds(35));
Text
spec:
  terminationGracePeriodSeconds: 45

If a process is killed early, no data is lost, but its running jobs wait for their leases to expire (60 seconds by default) before another worker takes them. Handlers should honour their CancellationToken so the cancel step finishes in time.

Map health checks: liveness on the live tag, readiness on ready. A draining worker is not ready but still alive, so the orchestrator stops routing to it without restarting it.

Capabilities

Schedules and rate limits need a capability turned on once per application, by an operator:

FeatureCapabilityError without it
AddScheduler, AddScheduleschedulerStartup fails with scheduler_not_active
RollingWindow(...) rate limitsrate_admissionDispatching a rate-limited job is refused with rate_admission_not_active; nothing is written

The order is: start the application once so it registers itself, activate, restart.

Text
runwick store activate-capability --name scheduler --application shop \
  --provider postgres --connection-env RUNWICK_MIGRATE_CS

Activation cannot be undone

Once raised, a capability stays. A binary that does not know it can no longer create work for that application. Activate only when every process that uses the store runs a release that supports it.

Checklist

  1. Pick the store and check its settings

    PostgreSQL 16 to 18 connected directly to the primary; SQLite on a local disk; Redis 8.2 or newer, one primary, with appendfsync always and noeviction. Runwick refuses unsafe settings at startup.

  2. Run migrations as their own step

    runwick migrate with the migration role, before the app starts. Use the CLI from the same release.

  3. Activate capabilities you need

    scheduler and rate_admission, once per application, after its first start.

  4. Set shutdown timeouts

    Runwick 25 s, host above that, container grace period above the host.

  5. Map health checks

    AddHealthChecks().AddRunwick(), with live and ready endpoints wired to your orchestrator.

  6. Secure the management API and dashboard

    Real sign-in, a grant mapping you have reviewed, HTTPS. Share the Data Protection key ring across replicas.

  7. Export telemetry

    Traces and metrics through OpenTelemetry. Turn on CollectApplicationMetrics in one process, import the Grafana dashboard and the alert rules, and tune the thresholds.

  8. Plan backups and restores

    Back up the store like any database you depend on, and read Upgrades before the first upgrade, not during it.