Skip to main content

Overview

Control Plane Task Runner is a self-hosted HTTP task queue and scheduler, similar to Google Cloud Tasks. You enqueue a task over HTTP and the workers deliver it to its target URL, with automatic retries, delayed and scheduled execution, per-client rate limiting and a circuit breaker. This template deploys the API, the workers, and the Redis Sentinel cluster they persist to.
This template deploys into an existing GVC that you already have. It does not create, provision or manage a GVC.
/v1/enqueue has no authentication, which is why api.public.enabled defaults to false. An unknown client_id is accepted and auto-registered rather than rejected, so a client ID is not a credential. With public access on, anyone who finds the endpoint can make a worker send outbound HTTP requests with a method, headers and body of their choosing. See Public Access and the Open Queue.

What Gets Created

The policies grant only the reveal permission on secrets. The template’s identities have no cloud access and no permission on any other Control Plane resource.

Prerequisites

One secret must exist before you install: the admin API key. The /admin/* endpoints create, edit and delete clients and rate-limit tiers, and they are guarded by the X-Admin-Key header. Secrets are org-level, so no GVC flag is involved.
1

Create the admin key secret

An opaque secret with encoding plain whose payload is the key itself:
Use printf, not echo. A trailing newline from echo becomes part of the key and then has to be sent in every admin request. Set api.admin.apiKeySecretName to this name.
2

Read the key back later

Pass -o yaml. A bare cpln secret reveal prints only a summary table, not the payload:
Create the secret before installing, or the deployment wedges silently. An install that names a secret which does not exist still reports success, and the API workload then never starts while the other workloads come up normally. The container never runs, so cpln logs returns zero lines. The one place the reason appears is status.versions[].message:
Use get-deployments. Plain cpln workload get has no versions key. Once the secret exists the workload recovers on its own after several minutes, or run cpln workload force-redeployment RELEASE_NAME-task-runner-api --gvc GVC_NAME to skip the wait.
Nothing else is required. The bundled Redis and Sentinel passwords are ordinary values, since only the API and worker containers use them, but they are used exactly as written, so replace the change-me- defaults at install time.

Installation

Create the admin key secret first, then install with its name and your own Redis and Sentinel passwords:
For the other install methods, follow the instructions for your preferred tool:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Image

The same image runs both the API and the worker:

API Workload

Setting api.admin.apiKeySecretName to "" turns admin authentication off entirely. The chart permits that only while api.public.enabled is false.

Worker Workload

Workers fetch whatever URL a task carries. allowPrivateUrls: false is what keeps an enqueued task off internal addresses.

Secrets and Redis

The admin API key is never in this secret. It is always your own opaque secret, whatever createSecret is set to.
  • secretName is an org-level name and is not prefixed with the release name. A second install in the same org needs a different secretName, with both fromSecret.name values changed to match.
  • To bring your own secret, set createSecret: false, create a dictionary secret holding the two keys named by the passwordKey fields, and point both fromSecret.name values at it:

Connecting

Read the public endpoint, when enabled:
To reach the private API from your machine, open a tunnel and check readiness. This uses the same /health/ready path as the workload readiness probe:
To see what happened to a delivered task, read the worker logs. The second form filters on the server, so put any text you expect in the line between the quotes:

Enqueuing Tasks

Post a task to /v1/enqueue. With the default private API, send it from a workload in the same GVC to http://RELEASE_NAME-task-runner-api.GVC_NAME.cpln.local:8080, or through the tunnel above to http://127.0.0.1:8080:
This request carries no credential, and none exists.

Admin Endpoints

Every /admin/* request needs the X-Admin-Key header, whose value is the payload of your api.admin.apiKeySecretName secret:

Rate Limiting

Rate limits apply per client, and a client tier is assigned through the admin API:

OpenTelemetry

Set otelEndpoint under api.env and worker.env to export traces. In the GVC settings, set the Tracing Provider to Control Plane, then use the built-in HTTP collector endpoint:

Operations

Backing Up

The Redis volume set RELEASE_NAME-redis-vs takes a snapshot every day and keeps each one for 7 days. List the snapshots:

Restoring a Backup

A restore procedure for this template has not been verified. Do not rely on the snapshots as a tested recovery path for queued tasks.

Rotating the Admin Key

Apply the whole secret with a new payload, confirm it, then force a redeployment of the API. A running replica keeps the old key until it is redeployed:
Every admin caller must then send the new key.

Upgrading From 1.2.x

Two defaults change, and both break an existing workflow that relied on the old behaviour. An upgrade that still carries api.env.adminApiKey or redis.admin.fromSecret, or an empty apiKeySecretName while public access is on, is rejected at render before anything is applied.
1

Create the admin key secret

Follow Prerequisites. Put the key you use today into it so existing admin callers keep working.
2

Drop the removed keys from your values

Remove api.env.adminApiKey and redis.admin.fromSecret, and set api.admin.apiKeySecretName instead.
3

Decide about public access

api.public.enabled now defaults to false. Read Public Access and the Open Queue before setting it back to true.
4

Upgrade

The first upgrade after an install re-applies the bundled Redis even with identical values, which restarts it, and the API returns errors briefly while Redis comes back. Later upgrades are clean.
The bundled Redis and Sentinel passwords keep whatever you already set. Only their defaults changed.

Public Access and the Open Queue

api.public.enabled is false by default. Turning it on exposes the following:
  • /v1/enqueue requires no credential. There is no header, token or signature to supply.
  • An unknown client_id is auto-registered, not rejected. A client ID is therefore not a secret and not a control.
  • The API becomes a request relay. Anyone reaching it can make a worker send outbound HTTP with the method, headers and body they choose. worker.env.allowPrivateUrls: false keeps those requests off internal addresses.
  • /metrics is unauthenticated and its labels list every client_id.
No setting closes this, because the application has no client authentication. If you need submissions from outside the GVC, put an authenticating proxy of your own in front of the API and leave api.public.enabled at false. Access changes can take several minutes to take effect, so re-test over that window.

Scaling and Availability

The API and worker are serverless workloads that autoscale on concurrent requests within api.replicas and worker.replicas. Raise worker.replicas.max or worker.env.concurrency for more delivery throughput. Redis runs as three replicas with three Sentinels, which elect a new primary if the current one fails.

Troubleshooting

Cause: the secret named by api.admin.apiKeySecretName does not exist. get-deployments shows The secret SECRET_NAME no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.Fix: create the secret as in Prerequisites. The workload recovers on its own, or run cpln workload force-redeployment RELEASE_NAME-task-runner-api --gvc GVC_NAME.
Cause: api.admin.apiKeySecretName is "" and public access is on. The chart refuses to publish unauthenticated admin endpoints.Fix: set api.admin.apiKeySecretName to your admin key secret, or set api.public.enabled: false.
Cause: your values still carry the 1.2.x admin key (api.env.adminApiKey or redis.admin.fromSecret).Fix: follow Upgrading From 1.2.x.
Cause: the X-Admin-Key header is missing or does not match the secret payload, for example because the key was created with echo and carries a trailing newline, or the secret was rotated without a redeployment.Fix: read the key with cpln secret reveal SECRET_NAME -o yaml, and redeploy the API after any rotation.
Cause: worker.env.allowPrivateUrls is false, so workers refuse private and internal targets.Fix: set it to true only if you accept that any enqueued task can then reach anything the worker can route to.

Important Notes

  • Create the admin key secret before installing. A missing secret wedges the API with no log output; the reason is in status.versions[].message.
  • Leave api.public.enabled at false unless an authenticating proxy fronts the API. /v1/enqueue is unauthenticated and auto-registers unknown client IDs.
  • Change the change-me- Redis and Sentinel passwords at install time. Once the volumes are initialised, changing them requires uninstalling (which deletes the volume sets) and reinstalling.
  • secretName is org-wide. Give each install in the same org its own secretName and matching fromSecret.name values.
  • Force a redeployment after rotating the admin key. A running replica keeps the old key.

External References

CPLN Task Runner Template

View the source files, default values, and chart definition

Task Runner Image

The container image used by the API and worker

Control Plane Secrets

Secret types and how workloads reference them

OpenTelemetry Documentation

OpenTelemetry distributed tracing documentation