Ctrl K
ComunidadGuías…

specs/080-restart-policy.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Spec 080: DAG Restart Policy

## Status

Implemented.

This spec defines conformance behavior for the DAG-level `restart_policy`
field: its values, how it combines with other DAG fields, and the validation
errors it produces.

## Scope

This spec covers:

- the `restart_policy` DAG field and its accepted values
- how `restart_policy` combines with `restart_wait_sec`, `schedule`,
  `retry_policy`, history retention, and the base configuration
- the validation errors of a DAG that sets `restart_policy`
- the `restart` command for a DAG that sets `restart_policy`
- runs started by hand next to an active run of a DAG that sets `restart_policy`

This spec does not define:

- when the scheduler starts the runs of a DAG that sets `restart_policy`,
  how they are dispatched, or timing guarantees; scheduler dispatch is
  outside this conformance suite
- the `restart_policy` field of the `container` block, which configures the
  Docker restart policy of a container
- `schedule.restart` and `restart_schedule`
- step-level retry or repeat behavior
- the web UI

## Goal

A workflow author can declare that a DAG is a long-running service, kept
running by Dagu instead of a cron schedule, a supervisor DAG, or an external
watchdog.

## Related Specs

- Built-in run context: [Spec 017](017-built-in-run-context.md), which defines
  the `restart` trigger type
- Schedule descriptors: [Spec 063](063-schedule.md)
- Repeat policy: [Spec 075](075-repeat-policy.md), which repeats a step inside
  one run

## Terms

A service DAG is a DAG whose `restart_policy` is `always` or `unless-stopped`.

## Behavior

### Field

`restart_policy` is a DAG-level field. It accepts:

| Value | Meaning |
| --- | --- |
| `no` | The DAG is not kept running. This is the default. |
| `always` | The DAG is kept running: a new run follows each run, `restart_wait_sec` after it ends, whatever its status. |
| `unless-stopped` | Same as `always`, except that no run follows a run that an operator stopped. |

Rules:

- Values are case-insensitive and surrounding whitespace is ignored.
- An empty value, or an absent field, means `no`.
- A base configuration cannot set `restart_policy` to `always` or
  `unless-stopped`: it applies to every DAG, so it would turn them all into
  services.

### Restrictions

A service DAG:

- must have a `restart_wait_sec` of at least 1, set in its file or inherited
  from the base configuration
- must not define a schedule in its file: `schedule`, in any of its forms,
  including `start`, `stop` and `restart`. An empty `schedule` defines none.
- must not define a DAG-level `retry_policy` in its file

A schedule or a DAG-level `retry_policy` that a service DAG would inherit from
the base configuration is ignored.

A service DAG may set `queue`, `timeout_sec`, `max_clean_up_time_sec`, history
retention, `handler_on`, `preconditions`, and any other field.

### History Retention

A service DAG adds a run to its history each time it is restarted, so days of
runs do not bound its history:

- a `hist_retention_days` in its file applies
- a `hist_retention_days` in the base configuration does not apply
- a `hist_retention_runs` in its file or in the base configuration applies

A service DAG left without a retention keeps its latest 100 runs, where other
DAGs keep 30 days of runs.

### Restart Command

The `restart` command fails for a service DAG, without stopping or starting a
run: the runs of a service DAG are started for it. The command reads the
current definition of the DAG, so it also fails for a DAG that has no run yet,
or whose `restart_policy` was set after its latest run started.

### Runs Started by Hand

A service DAG has one run at a time. While its latest run is running, queued or
waiting, or has not started for up to 5 minutes, the `start`, `enqueue` and
`retry` commands, their API requests and MCP actions, and webhooks fail without
starting a run. With no active run they work, which is how a stopped service
comes back.

The current definition of the DAG decides whether it is a service, also for a run
started from a run snapshot or from a spec in a request. The run that the
scheduler dispatches from the queue is refused only when another run of the DAG
has a live process on the same host; it then ends as failed. A run started under
another name with `--name` is a run of another DAG.

## Errors

Rules:

- A `restart_policy` that is not `no`, `always` or `unless-stopped` fails
  validation on the field `restart_policy`.
- A service DAG with a missing or non-positive `restart_wait_sec` fails
  validation on the field `restart_wait_sec`.
- A service DAG that defines a schedule fails validation on the field
  `restart_policy`.
- A service DAG that defines a DAG-level `retry_policy` fails validation on
  the field `restart_policy`.
- A base configuration that sets `restart_policy` to `always` or
  `unless-stopped` fails validation on the field `restart_policy`, and so does
  every DAG that uses it.
- The `restart` command for a service DAG exits with a non-zero status and an
  error that names `restart_policy`.
- A `start`, `enqueue` or `retry` command next to an active run of a service DAG
  exits with a non-zero status and an error that names `restart_policy`. The API
  responds with the status 409.

## Examples

A worker that is started again 5 seconds after each run ends:

```yaml
restart_policy: always
restart_wait_sec: 5
hist_retention_runs: 100
steps:
  - run: python3 worker.py
```

A server that stays down once an operator stops it:

```yaml
restart_policy: unless-stopped
restart_wait_sec: 10
hist_retention_runs: 100
steps:
  - run: ./serve.sh
```

A health probe that checks every 10 seconds within one run:

```yaml
restart_policy: always
restart_wait_sec: 15
hist_retention_runs: 100
steps:
  - run: ./check.sh
    repeat_policy:
      repeat: while
      exit_code: [0]
      interval_sec: 10
handler_on:
  failure:
    run: ./alert.sh
```

Rejected:

```yaml
restart_policy: always
restart_wait_sec: 5
schedule: "* * * * *"
steps:
  - run: echo tick
```

```yaml
restart_policy: always
steps:
  - run: echo tick
```

```yaml
restart_policy: sometimes
restart_wait_sec: 5
steps:
  - run: echo tick
```

## Conformance

`conformance/spec080_restart_policy/` checks the validation rules with static
fixtures, the `restart` command for a service DAG, and the commands that start a
run next to its active run.