-
Notifications
You must be signed in to change notification settings - Fork 0
HTTP API
yacron2 exposes an optional aiohttp REST control API,
enabled by adding a top-level web section to the configuration. It serves
endpoints for querying the daemon version, inspecting job status, starting and
cancelling jobs on demand, reading per-job run history, tailing captured job
output live, exposing Prometheus metrics, and (when
a cluster section is configured) reporting the cluster and fleet views. This page documents the configuration schema, every endpoint,
bearer-token authentication, Unix-socket permissions, and lifecycle behavior.
The interface is inherited from upstream yacron; web.authToken and
web.socketMode are yacron2 additions.
Looking for the browser UI? The same HTTP interface also serves the built-in Web Dashboard at
/on everyhttp://listener (enabled by default; disable it withui: false). This page documents the REST endpoints; the Web Dashboard page is the visual tour.
Add a web section with at least one listen URL:
web:
listen:
- http://127.0.0.1:8080
- unix:///tmp/yacron2.sockWindows:
unix://listeners are not supported on Windows. On Windows such a listen URL is skipped with the warningIgnoring web listen url <url>: unix-socket listeners are not supported on this platform(aiohttp'sUnixSiteneedscreate_unix_server, which the Windows Proactor loop lacks); use anhttp://listener instead. Thehttp://listener and the entire HTTP control API otherwise behave identically on Windows. See Running on Windows.
The server is created only when web.listen is non-empty. There must be exactly one
web block across the whole configuration: a duplicate web block in an included file
or a second file in a config directory raises a ConfigError. See
Includes, Defaults, and Multi-File Config.
The web section is parsed by the strictyaml CONFIG_SCHEMA in yacron2/config.py.
listen is required; the rest are optional (strictyaml Opt(...)).
| Option | Type | Default | Description |
|---|---|---|---|
listen |
sequence of strings | (required) | List of URLs to bind. Each is http://host:port or unix:///path. An empty list disables the server. |
headers |
map of string→string | (none) | Extra HTTP headers added to every 200 success response (all routes, including /cluster and /job-set-id) and to the 409 conflict body, but not the 404 or 401. |
authToken |
map (value/fromFile/fromEnvVar) |
(none) | When set, requires bearer-token authentication on all routes (see Authentication). |
socketMode |
string (octal) | (none) | File mode applied via chmod to unix:// listen sockets (see Unix socket permissions). Applies only to unix:// sockets, so it is irrelevant on Windows (where unix-socket listeners are unsupported and skipped with a warning). |
| Scheme | Form | Requirements |
|---|---|---|
http |
http://host:port |
Both host and port are required. An http URL missing either is logged as a warning (Ignoring web listen url ...: http url needs host and port) and skipped. |
unix |
unix:///path/to/socket |
Binds an aiohttp UnixSite at the given filesystem path. POSIX-only: on Windows UnixSite is unavailable (no create_unix_server on the Proactor loop), so such a URL is skipped with the warning Ignoring web listen url <url>: unix-socket listeners are not supported on this platform. Use an http:// listener instead. |
Any other scheme is logged (scheme ... not supported) and skipped. Binding maps to
web.TCPSite for http and web.UnixSite for unix (web_site_from_url in
yacron2/cron.py).
https is not a recognized scheme. To serve the API over TLS, bind to a loopback
http address or a unix socket (POSIX-only; on Windows use a loopback http
address) and terminate TLS in a reverse proxy.
All routes are registered in start_stop_web_app:
| Method | Path | Handler | Success status |
|---|---|---|---|
GET |
/version |
_web_get_version |
200 |
GET |
/job-set-id |
_web_job_set_id |
200 |
GET |
/cluster |
_web_get_cluster |
200 |
GET |
/fleet |
_web_get_fleet |
200 |
GET |
/status |
_web_get_status |
200 |
GET |
/jobs |
_web_list_jobs |
200 |
GET |
/jobs/{name}/runs |
_web_job_runs |
200 |
POST |
/jobs/{name}/start |
_web_start_job |
200 |
POST |
/jobs/{name}/cancel |
_web_cancel_job |
200 |
GET |
/jobs/{name}/logs |
_web_job_logs |
200 (SSE stream) |
GET |
/metrics |
_web_metrics |
200 (Prometheus exposition; omitted when metrics: false) |
GET |
/ |
_web_index |
200 (dashboard page; omitted when ui: false) |
The configured headers map is applied to every 200 success response across
all routes (including /cluster and /job-set-id) and to the 409 conflict
bodies of /jobs/{name}/start and /jobs/{name}/cancel. The 404 (unknown
job) and 401 (authentication failure) responses are raised without it.
The same interface serves the Web Dashboard at
/; that page is the visual tour of the UI these endpoints feed.
Returns the yacron2 version as text/plain (the value of yacron2.version.version).
$ http get http://127.0.0.1:8080/version
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
1.0.13Returns the status of every configured job. The response format depends on the
request's Accept header:
- If
Acceptis exactlyapplication/json, the response is a JSON array (application/json). - Otherwise the response is
text/plain, one job per line.
Each job has one of three statuses, determined in this order:
| Status | When | Fields |
|---|---|---|
running |
One or more instances are currently running. |
job, status, pid (list of the PIDs of running instances whose subprocess has been started, i.e. runjob.proc is not None). |
disabled |
The job is not running and enabled: false. |
job, status. |
scheduled |
The job is not running and is enabled. |
job, status, scheduled_in. |
For scheduled jobs, scheduled_in is the number of seconds until the next run
(a float, computed from the job's crontab in the job's timezone). For an @reboot
schedule, scheduled_in is the literal string "@reboot".
The disabled status is reported honestly instead of an inapplicable
scheduled (in N seconds).
Text form:
$ http get http://127.0.0.1:8080/status
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
test-01: scheduled (in 14 seconds)
test-02: running (pid: 12345)
test-03: disabledIn the text form, scheduled_in is rendered as a human-readable relative time
(in N seconds / minutes / hours / days), running jobs show
running (pid: <comma-separated pids>), and disabled jobs show disabled.
JSON form:
$ http get http://127.0.0.1:8080/status Accept:application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{"job": "test-01", "status": "scheduled", "scheduled_in": 6.16588},
{"job": "test-02", "status": "running", "pid": [12345]},
{"job": "test-03", "status": "disabled"}
]Returns this node's cluster view as JSON.
When no cluster section is configured, it returns
{"enabled": false, "peers": []}. When a cluster section is present it returns
enabled: true plus a backend field naming the active leadership backend
(gossip, kubernetes, or etcd) and the node's view: its
node_name and job_set_id, the computed cluster_size and quorum, whether
elect_leader is on, the distribution mode (single-leader or spread),
whether any conflict is pausing Leader jobs (conflict, the umbrella flag),
whether this node is quorate, the elected leader
(null when this node is not quorate, and always null in spread mode) and
is_leader (always false in spread mode), and a peers array (each with
host, status, node_name, job_set_id, last_seen, last_error, and
mismatch_streak). Under distribution: spread, per-job owners instead appear
as a clusterOwner field on each leader-gated job in GET /jobs.
The umbrella conflict flag is set by any of three triggers, each with its own
detail list, and all three stand Leader jobs down: a duplicate nodeName (the
offending names in conflict_names); an agreeing peer declaring a different
cluster size (size_conflict: true, the divergent sizes in conflicting_sizes);
and a quorate peer advertising a different distribution or elect_leader
setting, a coordination-policy conflict surfaced as policy_conflict: true with
the differing descriptors in conflicting_policies.
The lease backends (kubernetes / etcd) have no static peer set, so their
view is lease-shaped: backend names the backend, peers is empty,
cluster_size/quorum are 1, elect_leader is true, distribution is
single-leader, all three conflict flags (conflict, size_conflict,
policy_conflict) are always false, and an extra lease block carries the
backend-specific detail. This is an endpoint-reference view; the field
semantics (what quorate means for a lease backend, the lease block contents,
and the expiry rules below) are documented once in
Clustering and Leader Election.
For kubernetes the block is {name, namespace, identity, holder, expiry} and
for etcd {electionName, identity, holder, leaseId, expiry}. The expiry is
populated only while this node holds the lease: a follower reports
expiry: null. (For kubernetes it is the local lease deadline while this node
leads; for etcd it is the current lease deadline, or null when this node is
not the holder.) quorate is whether the node has a fresh successful read of the
lease store (stale → Leader fails closed; the never-skip PreferLeader default
then runs the job anyway).
$ http get http://127.0.0.1:8080/cluster Accept:application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"enabled": true,
"backend": "gossip",
"node_name": "node-a",
"job_set_id": "v1:…",
"cluster_size": 3,
"quorum": 2,
"elect_leader": true,
"distribution": "single-leader",
"conflict": false,
"conflict_names": [],
"size_conflict": false,
"conflicting_sizes": [],
"policy_conflict": false,
"conflicting_policies": [],
"quorate": true,
"leader": "node-a",
"is_leader": true,
"peers": [
{"host": "yacron-b.internal:8443", "status": "agreed", "node_name": "node-b", "job_set_id": "v1:…", "last_seen": "2026-06-23T19:00:00+00:00", "last_error": null, "mismatch_streak": 0}
]
}A lease backend (here kubernetes) returns the lease-shaped view instead:
The per-peer status values (agreed, syncing, drifted, unreachable,
untrusted, self, conflict, unknown) are documented in
Clustering and Leader Election.
The separate
GET /peerattestation endpoint is not part of this web API. It is served only on the cluster's own mutual-TLSlistenaddress (default port8443), never on the publicweblisteners. See Clustering and Leader Election.
Returns the cluster-wide per-job run view that backs the dashboard's
fleet view: one entry
per node, each carrying that node's per-job run summaries. It is answered
entirely from state this node already holds. Every gossip node piggybacks a
compact summary of its own jobs (running / enabled / seconds to next fire /
last finished run) on its mutual-TLS /peer response, so the summaries arrive
with the peer polls this node is already making; serving /fleet triggers no
peer traffic. Any node can therefore serve the whole fleet's picture, at most
one gossip interval stale per peer.
When no cluster section is configured, or the backend is a lease backend
(kubernetes / etcd, which carry only a lease and know nothing about what
other nodes run), it returns {"enabled": false, "nodes": []}.
For the gossip backend it returns enabled: true, the serving node's
node_name, the distribution and elect_leader policy, the gossip
interval in seconds (the peer-data freshness bound), and a nodes array.
The serving node is always first (self: true, status self, as_of stamped
at request time); each configured peer follows with the status and host
from the peer table and the summaries absorbed from its last successful poll
(as_of = last_seen). Self-listings are skipped and two addresses that
answer as the same process are deduplicated.
Per node: jobs maps each job name to
{running, enabled, scheduled_in, last}, where last is
{outcome, finished_at, duration, exit_code} or null for a job that has not
run since that node started. jobs: null (as opposed to {}) means no
snapshot is held for that node at all: it was never reached, or it runs an
older build that does not gossip summaries. truncated: true flags a node
with more jobs than the per-payload cap (512), whose advertised set is the
sorted-name prefix. A briefly unreachable peer keeps its last-known summaries
with the old as_of rather than being blanked, so stale data is visibly stale
instead of silently missing.
$ http get http://127.0.0.1:8080/fleet Accept:application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"enabled": true,
"backend": "gossip",
"node_name": "node-a",
"distribution": "spread",
"elect_leader": true,
"interval": 30,
"nodes": [
{"node_name": "node-a", "host": null, "self": true, "status": "self",
"as_of": "2026-06-23T19:00:02+00:00", "truncated": false,
"jobs": {"backup": {"running": false, "enabled": true, "scheduled_in": 1042.5,
"last": {"outcome": "success", "finished_at": "2026-06-23T18:00:01+00:00",
"duration": 12.4, "exit_code": 0}}}},
{"node_name": "node-b", "host": "yacron-b.internal:8443", "self": false, "status": "agreed",
"as_of": "2026-06-23T18:59:45+00:00", "truncated": false,
"jobs": {"backup": {"running": true, "enabled": true, "scheduled_in": null, "last": null}}}
]
}The summaries are observability data only: they never feed leader election or any run/skip decision, and a malformed or hostile peer payload degrades to "no data for that node" rather than poisoning the view.
Launches the named job immediately, regardless of its schedule. {name} is the
job's name.
| Condition | Response |
|---|---|
| No job with that name. |
404 Not Found. |
The job exists but has enabled: false. |
409 Conflict, body job '<name>' is disabled. |
| Otherwise. |
200 OK, empty body; the job is launched via the normal launch path. |
The 409 is deliberate: a disabled job behaves as if it is not there,
so the API refuses to launch it manually rather than overriding the config.
Manual launch goes through maybe_launch_job, so the job's concurrencyPolicy
applies. If an instance is already running, Allow starts another, Forbid does
not start a new one (the 200 still returns), and Replace cancels the running
instance(s) first. See Concurrency and Timeouts.
$ http post http://127.0.0.1:8080/jobs/test-02/start
HTTP/1.1 200 OK
Content-Length: 0Terminates every currently-running instance of the named job, using the same
graceful terminate-then-kill sequence yacron2 uses elsewhere (honoring the
job's killTimeout; see Concurrency and Timeouts).
Instances are cancelled concurrently, so a job with several running instances
costs at most one killTimeout, not one per instance.
| Condition | Response |
|---|---|
| No job with that name. |
404 Not Found. |
| The job exists but no instance is running. |
409 Conflict, body job '<name>' is not running. |
| Otherwise. |
200 OK, empty body; all running instances are cancelled. |
A run cancelled this way is recorded in the job's history with the outcome
cancelled. Cancellation is a deliberate operator action, not a job failure,
so it is not reported (onFailure does not fire) and does not trigger
retries.
$ http post http://127.0.0.1:8080/jobs/test-03/cancel
HTTP/1.1 200 OKReturns a JSON array describing every job: its schedule and timezone, whether it is enabled and running, the time until its next scheduled run, a summary of its most recent finished run, and a compact tail of recent outcomes. This is the endpoint the Web Dashboard polls.
| Field | Meaning |
|---|---|
name, enabled, schedule, command
|
The job's name and enabled flag, its schedule as a crontab string, and its command (argv lists are joined for display). |
captureStdout, captureStderr
|
Which output streams the job captures, and therefore which are available from /jobs/{name}/logs. |
utc, timezone
|
The schedule's reference frame: utc (default true) and the IANA timezone name, or null. |
running, pids
|
Whether any instance is currently running, and the PIDs of running instances whose subprocess has started. |
scheduled_in |
Seconds until the next scheduled run (a float), or null when not applicable (disabled, currently running, or a one-off @reboot schedule). |
last_run |
The most recent finished run (outcome, exit_code, started_at, finished_at, duration, fail_reason), or null if the job has not run yet. |
history |
Compact oldest-first tail of recent runs (outcome and duration only), sized for the dashboard's inline sparkline. Full per-run detail comes from /jobs/{name}/runs. |
clusterPolicy, clusterOwner
|
Present only when leader election is configured: the job's cluster policy, and, under distribution: spread for leader-gated jobs, the node that currently owns the job (null when there is no quorum). |
$ http get http://127.0.0.1:8080/jobs
[
{
"name": "test-01",
"enabled": true,
"schedule": "*/5 * * * *",
"command": "echo foobar",
"captureStdout": true,
"captureStderr": true,
"utc": true,
"timezone": "UTC",
"running": false,
"pids": [],
"scheduled_in": 42.1,
"last_run": {
"outcome": "success",
"exit_code": 0,
"started_at": "2026-06-21T12:00:00+00:00",
"finished_at": "2026-06-21T12:00:01+00:00",
"duration": 1.02,
"fail_reason": null
},
"history": [
{"outcome": "success", "duration": 0.98},
{"outcome": "failure", "duration": 1.21},
{"outcome": "success", "duration": 1.02}
]
}
]Returns the job's retained run history (oldest first, bounded, and held in
memory only) together with aggregate statistics. Returns 404 Not Found for
an unknown job.
Each entry in runs carries the same fields as last_run in GET /jobs
(outcome, exit_code, started_at, finished_at, duration,
fail_reason). stats summarizes them:
stats field |
Meaning |
|---|---|
total, success, failure, cancelled
|
Counts by outcome over the retained history. |
success_rate |
Success rate over runs that ran to completion. Cancellations are user-initiated, not a verdict on the job, so they are excluded; null when no run has completed. |
avg_duration, min_duration, max_duration, last_duration
|
Duration aggregates in seconds, over runs with a recorded duration; null when there are none. |
$ http get http://127.0.0.1:8080/jobs/test-01/runs
{
"name": "test-01",
"runs": [
{
"outcome": "success",
"exit_code": 0,
"started_at": "2026-06-21T12:00:00+00:00",
"finished_at": "2026-06-21T12:00:01+00:00",
"duration": 1.02,
"fail_reason": null
}
],
"stats": {
"total": 1,
"success": 1,
"failure": 0,
"cancelled": 0,
"success_rate": 1.0,
"avg_duration": 1.02,
"min_duration": 1.02,
"max_duration": 1.02,
"last_duration": 1.02
}
}A Server-Sent Events
stream of a job's captured output. Returns 404 Not Found for an unknown job.
The stream begins with the lines already buffered (from the most recent running
instance, or else the last finished run's retained output), then follows a
running job's new lines live, and finishes with an end event:
- Each line arrives as an
event: linewhosedatais a JSON object{"stream": "stdout"|"stderr", "line": "..."}. - When the run finishes, the server sends
event: endwith data{}. If the job has no captured output at all yet, the stream ends immediately withevent: endand data{"reason": "no-output"}. - During quiet periods the server writes an SSE comment (
: ping) every 15 seconds as a keep-alive, which also detects disconnected clients.
Only the streams a job captures (captureStdout / captureStderr) appear
here; see Output Capturing. The response carries
X-Accel-Buffering: no so reverse proxies (e.g. nginx) do not buffer the
stream.
$ curl -N http://127.0.0.1:8080/jobs/test-01/logs
event: line
data: {"stream": "stdout", "line": "foobar"}
event: end
data: {}Returns this instance's job-set id: the order-independent fingerprint of every
job's effective configuration that replicas compare to confirm they hold the
same set of jobs (see
the job-set id foundation).
The response is text/plain by default; with Accept: application/json it is
a JSON object that also carries the job count.
$ http get http://127.0.0.1:8080/job-set-id
v1:b834d7565aee0da50cd017f666651a5ba3b2e6b161daf0cb6e430f23f51ce90b
$ http get http://127.0.0.1:8080/job-set-id Accept:application/json
{"job_set_id": "v1:b834d7…51ce90b", "jobs": 3}Exposes yacron2's native Prometheus metrics: daemon
info, per-job run counters and duration histograms, live per-job gauges, and
(when a cluster section is configured) the cluster health series that mirror
GET /cluster. The exposition is generated by yacron2 itself, with no
exporter sidecar and no extra dependency. This section covers the endpoint
mechanics; the full metric reference lives in
Metrics with Prometheus.
The response format depends on the request's Accept header:
- By default the response is the classic Prometheus text format
(
text/plain; version=0.0.4). - If
Acceptadvertisesapplication/openmetrics-text, the response is OpenMetrics 1.0 (terminated by# EOF), as modern Prometheus servers request.
The configured web.headers are applied to the response as on every other
route, except Content-Type, which this endpoint owns: the exposition
format's contract always wins over an operator-configured header.
The endpoint is enabled by default whenever the web API is on. The
web.metrics option tunes or disables it, accepting either a boolean
shorthand (metrics: false disables the endpoint) or a map:
| Sub-option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Serve GET /metrics. |
public |
bool | false |
Exempt /metrics (and only /metrics) from web.authToken bearer-token authentication (see Authentication). |
durationBuckets |
sequence of floats | 0.1, 0.5, 1, 5, 15, 60, 300, 900, 3600 |
Upper bounds (seconds) of the yacron2_job_duration_seconds histogram. Bounds must be finite, positive, and strictly increasing; anything else raises a ConfigError. |
The metric registry is owned by the daemon rather than the web app, so counters survive config reloads (including ones that restart the web server) and reset only when the process restarts; series for jobs removed by a reload are pruned.
$ curl http://127.0.0.1:8080/metrics
# HELP yacron2_info yacron2 build information.
# TYPE yacron2_info gauge
yacron2_info{version="1.0.13"} 1
# HELP yacron2_jobs Number of configured jobs by enablement state.
# TYPE yacron2_jobs gauge
yacron2_jobs{state="enabled"} 2
yacron2_jobs{state="disabled"} 1
# HELP yacron2_job_runs_total Finished job runs by outcome, as recorded in the run history.
# TYPE yacron2_job_runs_total counter
yacron2_job_runs_total{job_name="test-01",status="success"} 12
yacron2_job_runs_total{job_name="test-01",status="failure"} 1
yacron2_job_runs_total{job_name="test-01",status="cancelled"} 0
...Serves the single-page Web Dashboard. Set ui: false in the
web section to disable the page and expose only the REST endpoints. The page
is served with secure default headers (a strict Content-Security-Policy,
anti-clickjacking, and nosniff) with any operator web.headers merged on top,
so a deliberately-set operator header wins. When web.authToken is enabled,
the page itself loads without a token (it holds no data); it prompts for the
token in the browser and authenticates every data request with it (see
Authentication).
The web.headers map (merged upstream but never released in yacron 0.19) is a
string→string map applied to every 200 success
response across all routes (/version, /status, /cluster, /job-set-id,
the job routes, and the 200 of /jobs/{name}/start) and to the 409
conflict bodies of /jobs/{name}/start and /jobs/{name}/cancel. It is not
applied to the 404 (unknown job) or 401 (authentication failure) responses,
which are raised without the configured headers. Example:
web:
listen:
- http://127.0.0.1:8080
headers:
X-Frame-Options: DENY
Cache-Control: no-storeBy default the API is unauthenticated; anyone who can reach a listen address can
call every endpoint. Restrict access at the network or socket level, or enable
bearer-token authentication with web.authToken.
authToken resolves the token from exactly one source, in this precedence order
(_resolve_web_token):
| Sub-option | Type | Description |
|---|---|---|
value |
string or null | Literal token value. |
fromFile |
string or null | Path to a file; the token is the file contents with surrounding whitespace stripped. |
fromEnvVar |
string or null | Name of an environment variable holding the token. |
When authToken is set, an aiohttp middleware (_make_auth_middleware) requires
Authorization: Bearer <token> on every route:
- The auth scheme is compared case-insensitively (
Bearer,bearer, etc.) per RFC 7235. - The presented token is compared against the configured token in constant time via
hmac.compare_digest. - A missing/malformed
Authorizationheader, a wrong scheme, or a non-matching token returns401 Unauthorized.
The one configurable exception: setting web.metrics.public: true exempts
/metrics (and only /metrics) from the bearer token, for scrapers that
cannot send credentials; every other route stays gated (see
GET /metrics).
web:
listen:
- http://127.0.0.1:8080
authToken:
fromEnvVar: YACRON2_WEB_TOKEN$ http get http://127.0.0.1:8080/status "Authorization:Bearer s3cr3t"
$ curl -H "Authorization: Bearer s3cr3t" http://127.0.0.1:8080/statusIf authToken is configured but resolves to an empty token, yacron2 raises a
ConfigError and refuses to start the web server, rather than silently serving
the control API with no authentication. This happens when:
-
value,fromFile, andfromEnvVarare all empty/absent; -
fromEnvVarnames a variable that is unset (resolves to""); -
fromFilepoints to a file that is empty or contains only whitespace.
If fromFile cannot be read (OSError), yacron2 also raises a ConfigError
(web.authToken.fromFile could not be read: ...).
Windows: this entire feature is POSIX-only. It depends on
unix://sockets (which Windows does not support) and onchmod. On WindowssocketModehas no effect andunix://listen URLs are skipped with a warning; use anhttp://listener. See Running on Windows.
web.socketMode is an octal-string file mode applied with chmod
to each unix:// listen socket after it starts (_apply_socket_mode):
web:
listen:
- unix:///run/yacron2/yacron2.sock
socketMode: "0660"- The mode is parsed as base-8 (
int(socketMode, 8)). - It is applied only to
unix://sockets; non-unixaddresses are ignored. - An invalid mode (not an octal integer, raising
ValueError) or achmodfailure (OSError) is logged as a warning (web: could not set socketMode <mode> on <path>: ...) and does not abort startup.
When using a Unix socket on a read-only-root container, point the socket at a small writable volume. See Production and Container Deployment.
The control API lifecycle is driven by start_stop_web_app, called on each config
reload from the scheduler loop:
- If a server is running and the new
webconfig is absent or differs from the running one, the running server is stopped (web_runner.cleanup()) before any new one is started. A change to anywebfield (includingheaders,authToken, orsocketMode) thus triggers a restart of the server on reload. - The server is (re)started only when
webis present,listenis non-empty, and no server is currently running. - Each
listenaddress is bound independently. A bad URL (ValueError) or a bind failure (OSError, e.g. address already in use) on one address is logged as a warning (web: could not listen on <addr>: ...) and skipped; the remaining addresses still bind, and the config update is not aborted. - The
web: started listening on <addr>log line is emitted only after the bind succeeds. - On shutdown, the running server is stopped after currently running jobs finish.
On Windows, graceful shutdown is triggered with Ctrl-C or Ctrl-Break (rather
than
SIGTERMas on POSIX); yacron2 still finishes currently-running jobs before stopping the server, identical to POSIX behavior. See Running on Windows.
A ConfigError raised while resolving authToken (empty token or unreadable
fromFile) propagates out of start_stop_web_app and is caught by the reload
loop's dedicated web-app handler, which logs Error in the web configuration, so not starting the web API and leaves the web API down until a later reload fixes
it. The rest of the new configuration (jobs, cluster, logging) is still applied:
the web app starts under its own error handling, after the cluster manager, so a
web misconfiguration cannot skip the cluster gate (which would otherwise fail
open and run every Leader job on every node).
- Web Dashboard: the built-in browser UI served by this interface.
-
Metrics with Prometheus: the full metric reference behind
GET /metricsand Prometheus scrape configuration. -
Clustering and Leader Election: the
GET /clusterview and the separate mTLS/peerendpoint. -
Running on Windows:
unix://listeners andsocketModebehave differently on Windows. - Configuration Reference
- CLI Reference
- Concurrency and Timeouts
- Production and Container Deployment
- Architecture and Internals
This wiki documents yacron2. See the README and the changelog.
yacron2 is a fork of gjcarneiro/yacron.
- Getting Started
- Configuration
- Job Behavior
- Integrations
- Reference and Development
{ "enabled": true, "backend": "kubernetes", "node_name": "yacron2-0", "job_set_id": "v1:…", "cluster_size": 1, "quorum": 1, "elect_leader": true, "distribution": "single-leader", "conflict": false, "conflict_names": [], "size_conflict": false, "conflicting_sizes": [], "policy_conflict": false, "conflicting_policies": [], "quorate": true, "leader": "yacron2-0", "is_leader": true, "peers": [], "lease": {"name": "yacron2-leader", "namespace": "default", "identity": "yacron2-0", "holder": "yacron2-0", "expiry": "2026-06-24T19:00:14.000000Z"} }