dagron/ docs
Operate

Update & upgrade

dagron migrates its own schema at engine startup and upgrades in place: back up, swap the binary or image, start. This page covers the order to roll a deployment, what a failed migration actually does, and how to go backwards.

Versioning policy

dagron follows Semantic Versioning and keeps a changelog in the Keep a Changelog format. One rule matters more than the rest: pre-1.0, a minor version bump means breaking changes. Treat 0.5.x0.6.0 the way you would treat a major-version upgrade elsewhere.

Release notes call out breaking changes explicitly and note when a version ships schema migrations. Reading the changelog is step 2 of the upgrade procedure below, not optional homework: it tells you whether the release migrates the database, and whether any API or configuration contract moved.

Never skip a changelog read on a minor bump. Until 1.0, patch releases (0.5.10.5.2) are the only ones guaranteed non-breaking.

How schema migrations work

Four facts decide every procedure on this page.

One deliberate asymmetry: dagron-api does not run migrations. It only creates its own tables with CREATE TABLE IF NOT EXISTS, so it starts happily against any database — including one restored from a partial dump, where it recreates its tables empty. Always back up and restore the whole database, never a table subset.

Upgrade procedure — single node

The stack upgrades in place: stop, swap the image or binary, start. In order:

  1. Back up. Always, before every upgrade — it is the only way backwards (see Rollback).
  2. Read the changelog. Note any migration or breaking change listed for the version.
  3. Stop ingestion. Pause schedules and stop whatever feeds the queue or submits runs.
  4. Let running work drain — or accept reclaim: task leases expire (default 30 s) and work is re-dispatched on restart, so tasks must be idempotent.
  5. Stop the engine and swap the binary or image.
  6. Start the engine. It applies any pending migrations at startup, inside a transaction, before serving.
  7. Verify. Exit code 0, GET /healthz on the ops API answers, and the migration ledger shows the expected version (panel below).
  8. Upgrade dagron-api, then the frontend, then resume ingestion.
STEP 1 — BACK UP FIRST
# Postgres — nightly, plus before every upgrade
$ pg_dump -U dagron -d workflow -Fc -f "workflow-$(date -u +%Y%m%dT%H%M%SZ).dump"

# SQLite — safe while the daemon is running (WAL)
$ sqlite3 workflow.db ".backup 'workflow-backup.db'"

Never copy a running SQLite workflow.db file by itself — the -wal/-shm sidecars carry committed data. Use .backup as above, or stop the daemon and copy all three files.

STEP 7 — VERIFY THE MIGRATION LEDGER
$ psql -U dagron -d workflow -tAc \
    "SELECT count(*)||' migrations, max='||max(version) FROM _sqlx_migrations;"

Compare the count and max version against a healthy install of the same release. If the engine exited 0 and /healthz answers, the migrations applied — a failed migration never leaves the engine running.

Upgrade procedure — multi-node and Helm

Multi-node engines (Postgres backend) coordinate only through the database — there is no RPC between nodes. That is why a rolling restart does not lose work: a stopped node’s task leases expire (default 30 s) and any surviving scheduler reclaims and re-dispatches the orphaned tasks. Singleton duties (cron, GC, DB-backed schedules) run on one leader; if the leader restarts, another node takes over when the leadership lease expires (LEADER_LEASE_SECS, default 30). See Scaling & HA for the model.

The order is the same as single-node, with one addition that matters:

  1. Back up, read the changelog, stop ingestion, drain or accept reclaim (steps 1–4 above).
  2. Upgrade ONE engine first. It applies the migrations at startup.
  3. Verify it — exit code 0, GET /healthz, ledger check — before touching the rest.
  4. Roll the remaining engines, then dagron-api, then the frontend.
  5. Resume ingestion.

Why one engine first: migrations are additive and applied under a transaction, but two engines starting simultaneously against an unmigrated database both try to apply the same set. One wins; the other may error and exit. Roll one, confirm, then the rest. Mixed versions during the roll are expected and safe for the duration of the roll — the schema is a superset and coordination is through database rows.

Helm

The chart pins matching image tags for every component, so moving the chart version moves the whole stack together:

HELM — ROLL TO A NEW RELEASE
$ helm upgrade dagron oci://registry-1.docker.io/mancube/dagron \
    --version <new-version>

Kubernetes rolls the engine pods; the first replacement engine to start applies the migrations, and the lease/reclaim behaviour above covers tasks in flight. The same caveat applies: tasks must be idempotent, because a reclaimed task may be re-dispatched.

Compose

The quickstart compose file pins every image to DAGRON_VERSION. Upgrading is setting the new version, pulling, and re-upping — after the same backup and changelog steps.

Rollback

Migrations are forward-only — there are no down migrations. Rollback = restore the pre-upgrade database backup and start the old binary. Everything written after that backup — runs, task history, schedule state — is discarded with it. This is why step 1 of every upgrade is a backup.

Two cases, in order of likelihood:

After any restore, runs that were running at backup time come back with expired leases; the reconcile loop reclaims and re-dispatches them. If a run must not re-execute, cancel it before starting the engine.

When an upgrade fails

The migration errors partway

The engine exits non-zero and the transaction rolls back, so the database stays at the previous version. Then:

  1. Read the error — it names the failing statement.
  2. If it is data-dependent (a constraint an existing row violates), fix the data and restart the engine. The migration re-runs from the top.
  3. If the migration itself is wrong, run the previous version’s binary — the schema is still where it was — and report it.
  4. Restore the pre-upgrade backup only if 1–3 leave you stuck; there is no partial state to clean up.

“previously applied but has been modified”

Startup fails with an error of this shape:

STARTUP REFUSED — CHECKSUM MISMATCH
Error: migration 40 was previously applied but has been modified

The binary’s copy of that migration differs from the checksum recorded when it was applied. The engine exits 1 and does not serve — the database is untouched, so there is no urgency to “fix” the DB. Fix the binary. Real causes, in order of likelihood:

CauseFix
edited migration fileSomeone edited a released migration. Revert the edit and ship a new migration instead — never edit an applied one.
mismatched buildsTwo builds from different commits (a fork, a patched vendor image). Deploy the binary whose migrations match, or roll forward with a build that supersedes the change.
tampered ledger rowThe recorded checksum itself was altered. Restore from backup.

Editing _sqlx_migrations to match the new checksum makes the error go away and leaves the schema silently not matching what the code expects. Only do it when you have confirmed the two migration bodies are semantically identical — and write down that you did.

Startup mentions an unknown or newer migration

Expected and tolerated: the ledger runs with ignore-missing semantics, so an engine that finds migration rows it does not ship (a newer release’s, or another edition’s) starts normally. If you see this as an error rather than a startup, you are on a build predating that fix — upgrade.

Where releases come from

Every release is tagged v*.*.* and published three ways.

Prebuilt binaries — GitHub Releases

GitHub Releases carries prebuilt archives of the dagron binary (default build: SQLite + ops API) plus a single SHA256SUMS file covering the whole set, so a download can be verified without trusting the transport. Linux builds are statically linked against musl, so they run on any distro.

TargetArchive
x86_64-unknown-linux-musltar.gz
aarch64-unknown-linux-musltar.gz
aarch64-apple-darwintar.gz
x86_64-pc-windows-msvczip

Only the root dagron binary is published as an archive — it is the one a person runs directly. dagron-api, the GitOps worker and the MCP server are services and ship as images. Platforms not listed (e.g. Intel macOS) can build from source with Cargo from the repository.

Container images — Docker Hub

Multi-arch (amd64/arm64) images publish under mancube/ on Docker Hub: mancube/dagron-engine, -api, -frontend, -gitops, and -mcp. A stable release vX.Y.Z tags each image :X.Y.Z, :X.Y, and :latest. Pin the full version tag in production so upgrades happen when you run the procedure above, not when a pull happens to land.

Helm chart — OCI

CHART LOCATION
oci://registry-1.docker.io/mancube/dagron

The chart version matches the release version, and the image tags inside the chart are pinned to the same version — one helm upgrade --version moves everything in step.