Overview
Control Plane Task Runner is a self-hosted task queue and scheduler service similar to Google Cloud Tasks. It provides HTTP-based task enqueuing with automatic retry, delayed and scheduled execution, per-client rate limiting, and multi-queue support with priority levels. The admin API key comes from a secret you create before installing, and the API is not exposed to the internet by default.What Gets Created
- API Workload — HTTP endpoint for enqueuing tasks, managing clients, and health checks. Scales between 1 and 3 replicas by default. Internal-only unless you turn public access on.
- Worker Workload — Background processor that picks tasks off the queue and executes them. Scales between 1 and 5 replicas by default. Always internal-only.
- Redis with Sentinel — A Redis instance with Redis Sentinel for high-availability task persistence and coordination. Sentinel monitors the Redis primary and handles automatic failover.
- Secret (optional, on by default) — A dictionary secret holding the bundled Redis and Sentinel passwords. The admin API key is not created here — it lives in the secret you create.
- Identity & Policy — An identity bound to the workloads with
revealon exactly the secrets they read: the Redis secret and your admin key secret.
Architecture
The API workload receives task submissions over HTTP and pushes them into Redis. The Worker workload polls Redis and executes tasks by making outbound HTTP requests to the configured target URLs. Both workloads connect to Redis via Sentinel for failover resilience. A fresh install reached all four workloads ready in about 58 seconds, and enqueue-to-delivery latency was under 5 seconds.This template does not create a GVC. You must deploy it into an existing GVC.
Prerequisites
The admin API key secret must exist before you install. 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 Set
plain whose payload is the key:api.admin.apiKeySecretName to the name you used. Use printf, not echo — echo appends a newline, which becomes part of the key and then has to be sent in every admin request.2
Read it back later
-o yaml is required; without it the command prints the secret’s metadata table rather than its payload:Admin authentication is enforced, and it is enforced on the internal path too. Verified on a live deployment:
/admin/clients returned 401 with no header and 401 with a wrong key, and 200 with the correct one — both over the public endpoint and over in-GVC service DNS.change-me-… defaults before installing.
Installation
Once the admin key secret exists, install the template using your preferred method: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
The defaultvalues.yaml for this template:
API Workload
api.enabled— Enable or disable the API workload.api.replicas— Min/max replica count for autoscaling (default: 1–3).api.port— Container port (default8080).api.public.enabled— Expose the API to the public internet. Defaults tofalse; read Public Access and the Open Queue before turning it on.api.public.pathPrefix— Optional path prefix for the public endpoint. Leave empty to serve from the root.api.admin.apiKeySecretName— Name of your pre-created opaque secret whose payload is the admin API key, sent as theX-Admin-Keyheader. Required;""disables admin authentication and is rejected while public access is on. See Prerequisites.api.resources— CPU and memory allocated to each API replica.api.env.logLevel— Log verbosity:debug,info,warn, orerror.api.env.connectRetries/api.env.retryIntervalSec— Redis connection retry behavior on startup.
Worker Workload
worker.enabled— Enable or disable the Worker workload.worker.replicas— Min/max replica count for autoscaling (default: 1–5).worker.resources— CPU and memory allocated to each Worker replica.worker.env.concurrency— Number of tasks a single Worker replica can execute concurrently.worker.env.taskTimeoutSec— Maximum duration in seconds before a task execution is considered failed (default1800).worker.env.maxRetry— Maximum number of retry attempts for a failed task.worker.env.allowPrivateUrls— Whentrue, allows tasks to target internal/private URLs. Workers fetch whatever URL a task carries, so leaving thisfalseis the only bound on where an enqueued task can reach.worker.env.cbFailureThreshold/worker.env.cbTimeoutSec— Circuit breaker settings to stop hammering failing endpoints.
Secrets and Redis
WhencreateSecret is true, the template creates a dictionary secret named by secretName holding the Redis and Sentinel passwords. The redis.*.fromSecret fields are pre-wired to reference it. The admin API key is not in this secret — it is always your own opaque secret, independent of createSecret.
When bringing your own secret (createSecret: false), create a dictionary secret holding the keys named by the passwordKey fields and point both fromSecret.name values at it. This path was verified end to end alongside the admin secret, including non-default key names.
Change
redis.redisPassword and redis.sentinelPassword before the first install — they are used exactly as written. They stay ordinary values deliberately: they are internal plumbing for a datastore bundled with this one app, which nobody connects to by hand. Once the volumes are initialised, changing them requires uninstalling (which deletes the volume sets) and reinstalling.Enqueuing Tasks
Submit tasks to the API. With the defaultapi.public.enabled: false the endpoint below is the in-GVC address RELEASE_NAME-task-runner-api.GVC_NAME.cpln.local:8080; it is a public *.cpln.app hostname only if you turned public access on.
Admin Endpoints
Every/admin/* request requires the X-Admin-Key header, whose value is the payload of your api.admin.apiKeySecretName secret:
Rate Limiting
Rate limits are applied per client and configured via the admin API. Available tiers:OpenTelemetry
To enable distributed tracing, setotelEndpoint in both api.env and worker.env. In your GVC configuration, ensure the Tracing Provider is set to Control Plane, then use the default HTTP collector endpoint:
Connecting
Find the public endpoint under
status.canonicalEndpoint of cpln workload get RELEASE_NAME-task-runner-api --gvc GVC_NAME -o yaml.
Upgrading From 1.2.x
Two behaviours change, and both will break an existing workflow if you relied on the old defaults.
To upgrade an existing install:
1
Create the admin key secret
Follow Prerequisites, putting 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. Leaving it at the new default closes the unauthenticated /v1/enqueue endpoint to the internet — read Public Access and the Open Queue before setting it back to true.4
Upgrade
The first upgrade after an install also re-applies the bundled Redis even with identical values, restarting it; the API returns errors for a minute or two while Redis comes back. Later upgrades are clean.
The bundled Redis and Sentinel passwords keep whatever you already set. Only their defaults changed, which affects fresh installs.
Public Access and the Open Queue
api.public.enabled is false by default. Understand exactly what turning it on exposes:
/v1/enqueuerequires no credential whatsoever. There is no header, token, or signature to supply.- An unknown
client_idis auto-registered, not rejected. Posting a never-before-seen ID returnsstatus: enqueuedand creates the client with the default tier. A client ID is therefore not a secret and not a control. - The consequence is a request relay. Anyone reaching the endpoint can make a worker issue outbound HTTP with the method, headers, and body they choose.
worker.env.allowPrivateUrls: falsekeeps those requests off internal addresses, which is the only bound. /metricsis served on the same port, unauthenticated, and its labels enumerate everyclient_id.
api.public.enabled at false.
Disabling public access surfaces as
421 Misdirected Request, not 403. After the change the public hostname stops routing entirely — a 421 here means the knob worked, not that something is broken. Access changes take roughly 30 seconds to a few minutes to propagate, so re-test over that window before concluding otherwise.Important Notes
- Create the admin key secret before installing. A missing prerequisite secret wedges the deployment with no log output at all; Prerequisites gives the one command that diagnoses it.
/v1/enqueueis unauthenticated and auto-registers unknown client IDs — this is why public access is off by default. See Public Access and the Open Queue./metricsis unauthenticated and its labels enumerate everyclient_id.- Change the
change-me-…Redis and Sentinel passwords before the first install. Changing them later requires uninstalling and reinstalling. - The first Helm upgrade after an install re-applies the bundled Redis even with identical values, which restarts it; the API returns errors for a minute or two while Redis comes back. Later upgrades are clean.
- Access changes take roughly 30 seconds to a few minutes to propagate, so a freshly toggled
api.public.enabledlooks unchanged at first — and once disabled, the public hostname returns421, not403.
External References
OpenTelemetry Documentation
OpenTelemetry distributed tracing documentation
CPLN Task Runner Template
View the source files, default values, and chart definition