Skip to main content
Tasks are in prerelease. To request this feature on your project, open this prepopulated support ticket and add your project ID before submitting.
A task is an on-demand, run-to-completion workload defined alongside your applications and services in .upsun/config.yaml. When triggered, a task container is injected into your environment’s cluster, runs a single command, and is removed when the command exits.

When to use a task

A task is the right choice when you need:
  • A background AI agent session — for example, an agent that reads your codebase, calls tools such as shell commands or database queries, and writes the results back to your project.
  • A one-time job that needs service access: database maintenance, a data export, a report.
  • A batch job triggered from your application rather than on a schedule.
A task is not the right choice for:
  • A long-running daemon: use a worker.
  • A scheduled job: use a cron.
  • Serving HTTP traffic: tasks are not routable and cannot be used as an upstream in routes:.

How tasks compare to other workloads

ApplicationWorkerCronTask
LifecycleAlways runningAlways runningScheduled, run-to-completionOn-demand, run-to-completion
HTTP exposureYesNoNoNo
Has its own image/buildYesNo (shares app slug)No (shares app slug)Yes
Triggered byDeployDeployScheduleAPI call
Blocks deploymentsN/AN/ACancelled on deployCancelled on deploy

Define a task

Tasks are declared at the top level of .upsun/config.yaml, alongside applications: and services:.

Required fields

FieldDescription
typeThe runtime image; same syntax as an application and the type in the preceding example.
run.commandThe command to execute. Runs to completion, not as a daemon. To run initialization steps that need service access, place them at the start of this command — for example: node setup.js && node agent.js.

Common optional fields

FieldDescription
source.rootSubdirectory containing the task’s code. Defaults to /.
run.timeoutMaximum duration in seconds. Defaults to 3600 (one hour); max value 86400 (one day). On timeout, the platform sends SIGTERM, then SIGKILL after a grace period.
authorizationsDeclared on a task: API permissions granted to the task container at runtime. Declared on an application: grants the app permission to trigger tasks and call the environment API.
hooks.buildCommand run during the environment build phase, producing a slug reused across runs.
relationshipsConnections to services and applications in the same cluster.
mountsWritable directories. instance and tmp mounts are reset between runs; use a storage or service mount to persist data across runs.
variablesEnvironment variables defined in your config file. Only use this for non-sensitive values — set secrets such as passwords and API keys using the API or CLI to keep them out of version control, for example: upsun variable:create --name MY_SECRET --sensitive 1.

Fields that don’t apply

web, workers, and crons have no meaning for tasks. Tasks have no HTTP router, are not long-running, and are not scheduled.

Relationships

A task can declare relationships to applications and services in the same environment: Inside the task container, these relationships appear in PLATFORM_RELATIONSHIPS exactly as they do for applications. The task can read from the database, write to the cache, and POST results back to the app over HTTP. Relationship direction is one-way. Applications and other tasks cannot declare a relationship to a task, because a task exists in the cluster only while a task is running. If you need the app to receive results from a task, the task should push them to the app, for example by using the app: “myapp:http” relationship.

Trigger a task

Tasks are triggered through the Upsun API (see the task run endpoint reference):
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  https://api.upsun.com/projects/{project}/environments/{env}/tasks/myagent/run
To pass run-time variables to the task, include a variables object in the request body:
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"variables": {"BATCH_SIZE": "100", "TARGET": "prod"}}' \
  https://api.upsun.com/projects/{project}/environments/{env}/tasks/myagent/run
$TOKEN is a short-lived access token obtained from your API token. See Authentication for the exchange process and API tokens for the steps to create one. Each invocation creates an activity (the same mechanism used for deploys, backups, and crons) that gives you:
  • A unique activity ID.
  • Live status (pending, in_progress, complete, cancelled).
  • Streamed logs through the existing activity logs endpoint.
  • Cancellation through the /cancel endpoint on the activity.

Trigger from application code

Declare which tasks the app is allowed to trigger using workload authorizations (the authorizations key), then request a short-lived token at runtime — no long-lived credentials required. Steps 1–3 break down each part individually. For a complete implementation combining token retrieval, caching, and triggering, see Sample implementation.

1. Declare the authorization

2. Request a token and trigger the task

token=$(curl http://localhost:8200/oauth2/token -d grant_type=client_credentials | jq -r .access_token)

curl -X POST -H "Authorization: Bearer $token" \
  "https://api.upsun.com/projects/$PLATFORM_PROJECT/environments/$PLATFORM_BRANCH/tasks/myagent/run"

3. Cache the token

Token caching is highly recommended if your application triggers tasks repeatedly. Without token caching, every trigger makes a round-trip to the auth proxy. Request a new token only when the current one is about to expire. By default, tokens expire after 60 seconds. The x-token-ttl header extends the lifetime up to 900 seconds (15 minutes) — set it to match your expected time between task triggers. For example, if your app triggers a task every 5 minutes (300 seconds), set x-token-ttl to slightly more than the trigger interval to account for network delays.

Sample implementation

The following examples combine steps 1–3 into a single helper that handles authorization declaration, token retrieval, caching, and task triggering.
import os
import time
import requests

_cache = {}

def _get_token():
    if _cache.get("expires_at", 0) - 30 > time.time():
        return _cache["token"]
    r = requests.post("http://localhost:8200/oauth2/token",
                      data={"grant_type": "client_credentials"},
                      headers={"x-token-ttl": "900"})  # 60–900 seconds
    r.raise_for_status()
    data = r.json()
    _cache["token"] = data["access_token"]
    _cache["expires_at"] = time.time() + data.get("expires_in", 900)
    return _cache["token"]

def trigger_task(task_name, variables=None):
    token = _get_token()
    project = os.environ["PLATFORM_PROJECT"]
    branch  = os.environ["PLATFORM_BRANCH"]
    r = requests.post(
        f"https://api.upsun.com/projects/{project}/environments/{branch}/tasks/{task_name}/run",
        headers={"Authorization": f"Bearer {token}"},
        json={"variables": variables} if variables else None,
    )
    r.raise_for_status()
    return r.json()

# Example: pass run-time variables
trigger_task("myagent", variables={"BATCH_SIZE": "100", "TARGET": "prod"})

Resources, variables, and access

Tasks follow the same model as applications:
  • Resources (CPU, memory): configured by using upsun resources:set against the task name. Billing is per-second for the duration of each run.
  • Variables and secrets: configured with upsun variable:create --level environment or by using the API. Sensitive variables are injected as environment variables at run time.
  • Access: standard project roles apply. Project admins and contributors can change task definitions, trigger tasks, and view runs.

Security

Tasks run in the same Linux Containers (LXC) as applications, with the same namespace isolation, capability dropping (removing unnecessary Linux process privileges), seccomp profile, cgroup limits, and network isolation. Cross-project isolation and relationship-based access control apply unchanged. For details, see the Project isolation topic. For tasks that run untrusted code (for example, an LLM-driven agent), consider pairing with bubblewrap inside the task container to restrict filesystem access, environment variables, and syscalls visible to the agent process.

Known limitations

  • Cancelled on deploy. A running task receives SIGTERM before any deploy on the same environment, followed by SIGKILL after a grace period of a few seconds. Catch SIGTERM, persist state to a service, and exit cleanly. The caller is responsible for retrying.
  • Concurrency cap. Multiple task runs can execute in parallel up to a default limit of 3. Further triggers queue behind running ones.
  • No task-to-task relationships. Nothing can declare a relationship to a task. An app and a task can share a relationship to the same service or a network file mount.
  • Fresh filesystem every run. instance and tmp mounts are reset between runs. Use a storage or service mount for files that must survive across runs.
  • Requires at least one application. A project with only tasks and services is not currently valid.
  • Minimal Console and CLI support. Task activities appear in the activity feed, but there is no dedicated task UI as part of this Prerelease program.
Last modified on June 8, 2026