Features
What Spindle actually does
No feature here exists because a competitor has it. Each one came out of a support ticket where somebody's nightly run went wrong in a way their crontab could not have told them about.
Scheduling
A workflow is triggered by a cron expression, a fixed interval, an inbound event, or by hand. The expression is standard five-field cron — there is nothing new to learn, and a crontab line can be pasted in unchanged.
Schedules are evaluated in a timezone you name, not in UTC and not in whatever the host happens to be set to. Daylight-saving transitions are handled explicitly: a run scheduled inside a skipped hour is moved forward, and a run inside a repeated hour fires once.
| Trigger | Written as | Notes |
|---|---|---|
| Cron | "20 2 * * *" | five fields, with a named timezone |
| Interval | every: 15m | measured from the end of the last run |
| Event | on: webhook | signed POST to a per-workflow URL |
| Manual | spindle run | CLI or console, with optional parameters |
| Chained | after: other-wf | starts when a named workflow passes |
Overlap policy
The question a crontab cannot answer: what if the previous run has not
finished? Choose per workflow — skip (the default), queue up to a
depth you set, cancel_previous, or allow for jobs that genuinely
parallelise.
Most incidents we see in migrated crontabs are two copies of the same job writing to the same table.
Dependency graphs
Steps declare needs, and Spindle derives the execution order. Independent
steps run in parallel up to the concurrency you allow; a step whose dependency failed is
reported as skipped, not failed, so the failure count in your history stays honest.
Fan-out and fan-in
A matrix step expands over a list — one region, one tenant, one shard per instance — and a downstream step can wait for all of them or for the first success.
Conditional steps
A step may be gated on an expression over previous outputs, so a cleanup step runs only when the import actually wrote something.
Cycle detection
A definition with a cycle is rejected when it is loaded, with the offending path named. It never reaches the scheduler.
Retries that mean something
Retry policy belongs to the step, because the right answer differs per step. An HTTP fetch should retry quickly and often; a migration should not retry at all; a charge should retry only with the same idempotency key.
retries:
max: 5
backoff: 20s
multiplier: 2.0 # 20s, 40s, 80s, 160s, 320s
jitter: 0.3
retry_on: [exit:75, timeout]
idempotency_key: "$SPINDLE_RUN_ID/$SPINDLE_STEP"
Exit codes can be classified: some mean "try again", some mean "this will never work", and some mean "the input was bad, skip it and carry on". A step that exits with a code you did not classify is treated as a hard failure.
Timeouts
Each step has a timeout, and the workflow has a wall-clock budget. On
timeout the agent sends SIGTERM, waits a grace period you set, then
SIGKILL — and records which of the two ended the process.
A step killed by timeout is retried only if
timeout is in its retry_on list.
Late runs
Separate from failure. late_after is a
budget for the whole workflow, and missing it pages even while every step is still green.
This is the alert that catches a gradually slowing job before it collides with the next
morning.
Observability
Agents and networking
The agent is a single static binary. It polls the control plane over outbound HTTPS, claims work, runs it as a local user you choose, and streams results back. There is no inbound connection to your network and no port to open.
| Item | Requirement |
|---|---|
| Platforms | Linux x86-64 and arm64, FreeBSD 14, macOS 14+ (development only) |
| Resident memory | ~40 MB idle, plus whatever your steps use |
| Outbound access | HTTPS to the control plane; no other egress required |
| Privileges | runs unprivileged; a system unit file is provided |
| Credentials | stay on the agent host; Spindle stores step names and exit codes |
Access control
Workflows belong to projects, and roles are granted on a project. The four roles are deliberately few: viewer reads history, operator may trigger and cancel runs, maintainer may change definitions, owner may change membership and billing.
Single sign-on through SAML or OIDC is available on the Scale plan, including group mapping onto project roles so access follows your directory rather than a separate list.
Limits
| Limit | Solo | Team | Scale |
|---|---|---|---|
| Workflows | 10 | 150 | unlimited |
| Steps per workflow | 20 | 100 | 400 |
| Concurrent steps | 4 | 40 | negotiated |
| Agents | 2 | 25 | unlimited |
| Log retention | 7 days | 30 days | 180 days |
| Run history | 90 days | 1 year | 3 years |
| Minimum interval | 5 min | 1 min | 15 sec |
| Artifact size per run | 10 MB | 100 MB | 1 GB |
Hitting a limit never silently drops a run: the run is queued and the project owner is notified. Details on the pricing page.