tork is a free, open source orchestration & scheduling project written in Go and released under MIT. It has 817 GitHub stars, 63 forks and 20 open issues, and was last pushed 2 months ago. On this registry it ranks #64 of 64 tracked projects in Orchestration & Scheduling, with 5 head-to-head comparisons available.

What is tork?

What it is

Tork is a lightweight, distributed workflow engine written in Go and released under the MIT license. It runs each task of a job as a script inside its own container, so work is defined in YAML rather than as compiled code. The project lives in the Go ecosystem and uses Docker, Podman, or a plain shell as its task runtime, with PostgreSQL as the datastore and a message broker such as RabbitMQ for distributed operation.

The concrete problem it solves is coordinating multi-step work across machines without asking operators to build that coordination themselves. A job can consist of multiple tasks, and Tork handles scheduling, isolation, retries, timeouts, conditional and parallel execution, sub-jobs, looping tasks, priorities, and secrets redaction. Because coordinators are stateless and leaderless, adding workers increases throughput and a crashed worker does not strand the task it was running; Tork recovers that task automatically. Jobs, status, and results are reachable through a REST API, so the engine also serves as a backend for other systems.

Key capabilities

  • REST API for submitting jobs, querying status, and cancelling or restarting them.
  • Horizontally scalable execution: adding workers increases the number of tasks handled at once.
  • Task isolation in containers, with resource limits and idempotency, running on Docker, Podman, or a shell runtime.
  • Automatic recovery of tasks when a worker crashes, plus configurable retries of failed tasks for up to ten attempts.
  • Stateless, leaderless coordinators, so the control plane has no single point of failure.
  • Middleware hooks at the HTTP, Job, Task, and Node levels for authentication, logging, and metrics.
  • Job definition features including conditional tasks, parallel task, each task for looping, sub-job task, pre and post tasks, expression language, secrets with auto-redaction, and cron-scheduled jobs.

Who uses it and how

  • Teams running standalone mode on a single machine, where one Tork process handles scheduling and task execution together.
  • Operators running distributed mode with a Coordinator that schedules work and one or more Workers that execute it, connected by a message broker.
  • Pipelines that share data between tasks by writing to external object storage such as MinIO or S3, since container filesystems are discarded when a task ends.
  • Integrations that submit YAML job definitions over the REST API and poll job state, or that subscribe to webhooks on job and task state changes.
  • Administrators who monitor and submit jobs through Tork Web, the bundled web UI.

Getting started

Download the Tork binary from the releases page, start a PostgreSQL container and run the migration with the TORK_DATASTORE_TYPE=postgres environment variable, then launch ./tork run standalone. Distributed mode additionally starts RabbitMQ and runs ./tork run coordinator and ./tork run worker. No hosted option is listed.

When to use it — and when not to

Tork fits teams that want container-isolated, retryable workflows they can submit over an API and scale by adding workers, without adopting a large managed platform. A self-hoster must operate the supporting services the README names: PostgreSQL for the datastore, and for distributed mode a broker such as RabbitMQ plus external storage such as MinIO or S3 when tasks need to share data. Tasks are ephemeral by design, so any state outside the container must be pushed to that external store, and the project carries

project readme (upstream, from github) — read inline


FeaturesQuick StartInstallationArchitectureJobsTasksConfigurationREST APIWeb UIExtend

Tork is a highly-scalable, general-purpose workflow engine. It lets you define jobs consisting of multiple tasks, each running inside its own container. You can run Tork on a single machine (standalone mode) or set it up in a distributed environment with multiple workers.

Features

tork CLI

  • REST API – Submit jobs, query status, cancel/restart
  • Horizontally scalable – Add workers to handle more tasks
  • Task isolation – Tasks run in containers for isolation, idempotency, and resource limits
  • Automatic recovery – Tasks are recovered if a worker crashes
  • Stand-alone and distributed – Run all-in-one or distributed with Coordinator + Workers
  • Retry failed tasks – Configurable retry (up to 10 attempts)
  • MiddlewareHTTP, Job, Task, Node middleware for auth, logging, metrics
  • No single point of failure – Stateless, leaderless coordinators
  • Task timeoutTimeout per task
  • Full-text search – Search jobs via the API
  • Runtime agnosticDocker, Podman, Shell
  • Webhooks – Notify on job/task state changes
  • Pre/Post tasksPre/Post tasks for setup/teardown
  • Expression languageExpressions for conditionals and dynamic values
  • Conditional tasks – Run tasks based on if conditions
  • Parallel tasksParallel Task
  • Each taskEach Task for looping
  • Subjob taskSub-Job Task
  • Task priorityPriority (0–9)
  • SecretsSecrets with auto-redaction
  • Scheduled jobsScheduled jobs with cron
  • Web UITork Web for viewing and submitting jobs

Quick Start

Requirements

  1. A recent version of Docker.
  2. The Tork binary from the releases page.

Set up PostgreSQL

Start a PostgreSQL container:

Note: For production, consider a managed PostgreSQL service for better reliability and maintenance.

docker run -d \
  --name tork-postgres \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=tork \
  -e POSTGRES_USER=tork \
  -e PGDATA=/var/lib/postgresql/data/pgdata \
  -e POSTGRES_DB=tork postgres:15.3

Run the migration to create the database schema:

TORK_DATASTORE_TYPE=postgres ./tork migration

Hello World

Start Tork in standalone mode:

./tork run standalone

Create hello.yaml:

# hello.yaml
---
name: hello job
tasks:
  - name: say hello
    image: ubuntu:mantic
    run: |
      echo -n hello world
  - name: say goodbye
    image: alpine:latest
    run: |
      echo -n bye world

Submit the job:

JOB_ID=$(curl -s -X POST --data-binary @hello.yaml \
  -H "Content-type: text/yaml" http://localhost:8000/jobs | jq -r .id)

Check status:

curl -s http://localhost:8000/jobs/$JOB_ID
{
  "id": "ed0dba93d262492b8cf26e6c1c4f1c98",
  "state": "COMPLETED",
  ...
}

Running in distributed mode

In distributed mode, the Coordinator schedules work and Workers execute tasks. A message broker (e.g. RabbitMQ) moves tasks between them.

Start RabbitMQ:

docker run \
  -d -p 5672:5672 -p 15672:15672 \
  --name=tork-rabbitmq \
  rabbitmq:3-management

Note: For production, consider a dedicated RabbitMQ service.

Run the coordinator:

TORK_DATASTORE_TYPE=postgres TORK_BROKER_TYPE=rabbitmq ./tork run coordinator

Run one or more workers:

TORK_BROKER_TYPE=rabbitmq ./tork run worker

Submit the same job as before; the coordinator and workers will process it.

Adding external storage

Tasks are ephemeral; container filesystems are lost when a task ends. To share data between tasks, use an external store (e.g. MinIO/S3).

Start MinIO:

docker run --name=tork-minio \
  -d -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=minioadmin \
  -e MINIO_ROOT_PASSWORD=minioadmin \
  minio/minio server /data \
  --console-address ":9001"

Example job with two tasks (write to MinIO, then read back):

name: stateful example
inputs:
  minio_endpoint: http://host.docker.internal:9000
secrets:
  minio_user: minioadmin
  minio_password: minioadmin
tasks:
  - name: write data to object store
    image: amazon/aws-cli:latest
    env:
      AWS_ACCESS_KEY_ID: "{{ secrets.minio_user }}"
      AWS_SECRET_ACCESS_KEY: "{{ secrets.minio_password }}"
      AWS_ENDPOINT_URL: "{{ inputs.minio_endpoint }}"
      AWS_DEFAULT_REGION: us-east-1
    run: |
      echo "Hello from Tork!" > /tmp/data.txt
      aws s3 mb s3://mybucket
      aws s3 cp /tmp/data.txt s3://mybucket/data.txt

  - name: read data from object store
    image: amazon/aws-cli:latest
    env:
      AWS_ACCESS_KEY_ID: "{{ secrets.minio_user }}"
      AWS_SECRET_ACCESS_KEY: "{{ secrets.minio_password }}"
      AWS_ENDPOINT_URL: "{{ inputs.minio_endpoint }}"
      AWS_DEFAULT_REGION: us-east-1
    run: |
      aws s3 cp s3://mybucket/data.txt /tmp/retrieved.txt
      echo "Contents of retrieved file:"
      cat /tmp/retrieved.txt

Installation

Download the Tork binary for your system from the releases page.

Create a directory and unpack:

mkdir ~/tork
cd ~/tork
tar xzvf ~/Downloads/tork_0.1.66_darwin_arm64.tgz
./tork

You should see the Tork banner and help. On macOS you may need to allow the binary in Security & Privacy settings.

PostgreSQL and migration

See Quick Start – Set up PostgreSQL and run:

TORK_DATASTORE_TYPE=postgres ./tork migration

Standalone mode

./tork run standalone

Distributed mode

Configure the broker (e.g. in config.toml):

# config.toml
[broker]
type = "rabbitmq"

[broker.rabbitmq]
url = "amqp://guest:guest@localhost:5672/"

Start RabbitMQ, then:

./tork run coordinator
./tork run worker

Queues

Tasks go to the default queue unless overridden. Workers subscribe to queues; you can run multiple consumers per queue:

# config.toml
[worker.queues]
default = 5
video = 2

[broker]
type = "rabbitmq"

Route a task to a specific queue:

name: transcode a video
tasks:
  - name: transcode video
    queue: video
    image: jrottenberg/ffmpeg:3.4-alpine
    run: |
      ffmpeg -i https://example.com/some/video.mov output.mp4

Architecture

A workflow is a job: a series of tasks (steps) run in order. Jobs are usually defined in YAML:

---
name: hello job
tasks:
  - name: say hello
    image: ubuntu:mantic
    run: echo -n hello world
  - name: say goodbye
    image: ubuntu:mantic
    run: echo -n bye world

Components:

  • Coordinator – Tracks jobs, dispatches work to workers, handles retries and failures. Stateless and leaderless; does not run tasks.
  • Worker – Runs tasks via a runtime (usually Docker).
  • Broker – Routes messages between Coordinator and Workers.
  • Datastore – Persists job and task state.
  • Runtime – Execution environment for tasks (Docker, Podman, Shell).

Jobs

A job is a list of tasks executed in order.

Simple example

name: hello job
tasks:
  - name: say hello
    var: task1
    image: ubuntu:mantic
    run: |
      echo -n hello world > $TORK_OUTPUT
  - name: say goodbye
    image: ubuntu:mantic
    run: |
      echo -n bye world

Submit:

curl -s -X POST --data-binary @job.yaml \
  -H "Content-type: text/yaml" \
  http://localhost:8000/jobs

Inputs

name: mov to mp4
inputs:
  source: https://example.com/path/to/video.mov
tasks:
  - name: convert the video to mp4
    image: jrottenberg/ffmpeg:3.4-alpine
    env:
      SOURCE_URL: '{{ inputs.source }}'
    run: |
      ffmpeg -i $SOURCE_URL

readme truncated — read the full docs on github

Frequently asked questions

Is tork free to use?

tork is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does tork do?

Tork is a lightweight, distributed workflow engine that runs tasks as simple scripts within Docker containers.

What is tork written in?

tork is primarily written in Go. Its source is publicly available at https://github.com/runabol/tork, and it has 817 GitHub stars.