Docs / Queues

Queues

Queues define execution policies — concurrency, rate limiting, retries, and backoff strategies.

Queue Configuration

Every queue has a QueueConfig that controls how tasks in that queue are executed.

FieldTypeDefaultDescription
concurrencynumber10Maximum concurrent running tasks
max_retriesnumber3Default max retries per task
timeout_secsnumber300Default task timeout in seconds
backoffobjectExponential 1000msDefault backoff strategy for retries
rate_limitobjectnullOptional token-bucket rate limit
retention_secsnumbernullAuto-delete terminal flows older than this (seconds). Null = keep forever.
secretsobject{}Named secrets for ${secrets.*} interpolation in executor configs (see Secrets)

Example: creating a queue

curl -X POST http://localhost:8080/api/v1/queues \
  -H "Content-Type: application/json" \
  -d '{
    "id": "email-sends",
    "config": {
      "concurrency": 5,
      "max_retries": 5,
      "timeout_secs": 60,
      "backoff": { "exponential_jitter": { "initial_delay_ms": 2000 } },
      "rate_limit": { "max_burst": 20, "per_second": 10.0 }
    }
  }'

Fields you omit fall back to defaults. A minimal queue only needs an id:

curl -X POST http://localhost:8080/api/v1/queues \
  -H "Content-Type: application/json" \
  -d '{ "id": "default" }'

Concurrency Control

Each queue enforces a concurrency limit via a per-queue semaphore. This controls the maximum number of tasks running in parallel across all flows in that queue.

{
  "id": "heavy-compute",
  "config": {
    "concurrency": 5
  }
}

With concurrency: 5, at most 5 tasks from this queue will be running at any given time. Other ready tasks wait until a slot opens. This applies across all flows submitted to the queue.

Rate Limiting

Queues support optional rate limiting using a token bucket algorithm. This is useful when your tasks call external APIs with rate limits.

FieldTypeDescription
max_burstnumberToken bucket capacity. Max tasks that can start in a burst.
per_secondnumberToken refill rate. Sustained tasks dispatched per second.
{
  "id": "api-calls",
  "config": {
    "rate_limit": {
      "max_burst": 20,
      "per_second": 10.0
    }
  }
}

This allows bursts of up to 20 tasks, then sustains 10 tasks per second. The bucket refills continuously at the per_second rate. If no rate limit is configured, tasks are dispatched as fast as the concurrency limit allows.

Retry & Backoff

When a task fails with retryable: true, the engine schedules a retry after a delay determined by the backoff strategy. Tasked supports three strategies:

Fixed

Constant delay between retries.

{
  "fixed": {
    "delay_ms": 1000
  }
}

Every retry waits exactly delay_ms milliseconds. Use this when you want predictable, uniform retry intervals.

Exponential

Delay doubles with each retry attempt: initial_delay_ms * 2^attempt.

{
  "exponential": {
    "initial_delay_ms": 1000
  }
}

With initial_delay_ms: 1000, retries wait 1s, 2s, 4s, 8s, and so on. This is the default backoff strategy.

Exponential with Jitter

Exponential backoff plus random jitter (50-150% of the base delay). This prevents thundering herd problems when many tasks retry simultaneously.

{
  "exponential_jitter": {
    "initial_delay_ms": 1000
  }
}

With initial_delay_ms: 1000, the first retry waits between 500ms and 1500ms, the second between 1s and 3s, and so on.

Retry count

The max_retries setting controls how many times a task can be retried before being marked as permanently failed. Each task tracks its own remaining retry count.

Retention

Queues can optionally set a retention_secs to automatically clean up terminal flows (succeeded, failed, or cancelled) that are older than the specified age. This prevents unbounded storage growth for high-throughput queues.

FieldTypeDefaultDescription
retention_secsnumbernullSeconds to keep terminal flows. null = keep forever.

The engine runs a cleanup sweep every hour (configurable via cleanup_interval in engine config). Any flow in a terminal state whose updated_at is older than retention_secs is deleted along with its tasks.

Example: 7-day retention

{
  "id": "ephemeral-jobs",
  "config": {
    "concurrency": 10,
    "retention_secs": 604800
  }
}

Flows in the ephemeral-jobs queue are automatically deleted 7 days after reaching a terminal state. Running flows are never affected by retention cleanup.

Secrets

Queues can define named secrets that are resolved at runtime and interpolated into executor configs using ${secrets.<name>} syntax. This keeps sensitive values like API keys out of flow definitions.

Each secret is a SecretRef that specifies where to load the value from:

SourceTypeDescription
envstringRead the secret from an environment variable
filestringRead the secret from a file path (trimmed of trailing whitespace)

Example: queue with secrets

{
  "id": "ai-queue",
  "config": {
    "concurrency": 5,
    "secrets": {
      "ANTHROPIC_API_KEY": { "env": "ANTHROPIC_API_KEY" },
      "DEPLOY_TOKEN": { "file": "/run/secrets/deploy-token" }
    }
  }
}

Tasks in this queue can reference these secrets in their executor config:

{
  "id": "call-ai",
  "executor": "agent",
  "config": {
    "provider": "claude",
    "prompt": "Summarize this document",
    "env": { "ANTHROPIC_API_KEY": "${secrets.ANTHROPIC_API_KEY}" }
  }
}

The engine resolves ${secrets.ANTHROPIC_API_KEY} by reading the ANTHROPIC_API_KEY environment variable (as configured in the queue's secrets map) and substituting the value before dispatching the task.

Per-Task Overrides

Tasks can override the queue's default timeout_secs, retries, and backoff in the flow definition. The queue config provides the defaults; task-level values take precedence.

{
  "tasks": [
    {
      "id": "quick-check",
      "executor": "http",
      "config": { "url": "https://api.example.com/health", "method": "GET" },
      "timeout_secs": 5,
      "retries": 1,
      "backoff": { "fixed": { "delay_ms": 500 } }
    },
    {
      "id": "long-job",
      "executor": "shell",
      "config": { "command": "./process-data.sh" },
      "timeout_secs": 3600,
      "retries": 0
    }
  ]
}

Here quick-check uses a 5-second timeout with 1 retry and fixed backoff. long-job has an hour-long timeout and no retries, regardless of queue defaults.

On this page