> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure tasks to run on-demand workloads and AI agents

> Run on-demand workloads like AI agents and batch jobs alongside your apps and services, triggered from the Console or through the API.

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.

For working examples, see the [Upsun Cloud performance agent](https://developer.upsun.com/docs/get-started/ai/upsun-cloud-performance-agent.md) and the [Upsun Cloud log-analyzer agent](https://developer.upsun.com/docs/get-started/ai/upsun-cloud-log-analyzer-agent.md), which each run as a task to analyze observability data and generate a report.

A task is *not* the right choice for:

* **A long-running daemon**: use a [worker](https://developer.upsun.com/docs/configure-apps/image-properties/workers.md#workers).
* **A scheduled job**: use a [cron](https://developer.upsun.com/docs/configure-apps/image-properties/crons.md).
* **Serving HTTP traffic**: tasks are not routable and cannot be used as an upstream in `routes:`.

## How tasks compare to other workloads

| | Application | Worker | Cron | Task |
| :- | :- | :- | :- | :- |
| Lifecycle | Always running | Always running | Scheduled, run-to-completion | On-demand, run-to-completion |
| HTTP exposure | Yes | No | No | No |
| Has its own image/build | Yes | No (shares app slug) | No (shares app slug) | Yes |
| Triggered by | Deploy | Deploy | Schedule | API call |
| Blocks deployments | N/A | N/A | Cancelled on deploy | Cancelled on deploy |

## Define a task

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


&#x20;   applications:
&#x20;     myapp:
&#x20;       type: php:\{\{version:php:latest}}
&#x20;       relationships:
&#x20;         database: \{}
&#x20;       authorizations:
&#x20;         \- type: task # allow the app to trigger and operate this task
&#x20;           resource: myagent
&#x20;           action: operate
&#x20;         \- type: env # allow the app to access the environment API (read-only)
&#x20;           action: view
&#x20;       \# ...

&#x20;   services:
&#x20;     database:
&#x20;       type: postgresql:\{\{version:postgresql:latest}}

&#x20;   tasks:
&#x20;     myagent:
&#x20;       source:
&#x20;         root: /agent
&#x20;       type: nodejs:\{\{version:nodejs:latest}}
&#x20;       run:
&#x20;         command: node agent.js
&#x20;         timeout: 3600
&#x20;       authorizations: # allow the task to access the environment API (read-only)
&#x20;         \- type: env
&#x20;           action: view
&#x20;       hooks:
&#x20;         build: npm ci
&#x20;       relationships:
&#x20;         database: \{}
&#x20;         app: "myapp:http"
&#x20;&#x20;

### Required fields

| Field | Description |
| :- | :- |
| `type` | The runtime image; same syntax as an application and the `type` in the preceding example. |
| `run.command` | The 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

| Field | Description |
| :- | :- |
| [`source.root`](https://developer.upsun.com/docs/configure-apps/image-properties/source) | Subdirectory containing the task's code. Defaults to `/`. |
| `run.timeout` | Maximum 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. |
| [`authorizations`](https://developer.upsun.com/docs/configure-apps/image-properties/authorizations) | Declared 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.build`](https://developer.upsun.com/docs/configure-apps/image-properties/hooks) | Command run during the environment build phase, producing a slug reused across runs. |
| [`relationships`](https://developer.upsun.com/docs/configure-apps/image-properties/relationships) | Connections to services and applications in the same cluster. |
| [`mounts`](https://developer.upsun.com/docs/configure-apps/image-properties/mounts) | Writable directories. `instance` and `tmp` mounts are reset between runs; use a `storage` or `service` mount to persist data across runs. |
| [`variables`](https://developer.upsun.com/docs/configure-apps/image-properties/variables) | Environment 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](https://developer.upsun.com/docs/development/variables/set-variables.md) to keep them out of version control, for example: `upsun variable:create --name MY_SECRET --sensitive 1`. |

### Fields that don't apply

[`web`](https://developer.upsun.com/docs/configure-apps/image-properties/web), [`workers`](https://developer.upsun.com/docs/configure-apps/image-properties/workers), and [`crons`](https://developer.upsun.com/docs/configure-apps/image-properties/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:


&#x20;   tasks:
&#x20;     myagent:
&#x20;       relationships:
&#x20;         database: \{}      # e.g. PostgreSQL service
&#x20;         cache: \{}         # e.g. Redis service
&#x20;         app: "myapp:http" # application

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

You can trigger a task from the Console, the Upsun Cloud CLI, or through the Upsun Cloud API.

### From the Console

1. Open your project or environment overview page.
2. Find the **Tasks** card, below **Apps & Services**.
3. Hover over the task you want to run to open its detail popover.
4. Click **Run task**, optionally provide runtime variables as JSON, and confirm.

The task's status changes to **Running**, then back to **Idle** when it completes.

### From the CLI

Run a task using `upsun task:run`:

```bash theme={null}
upsun task:run myagent
```

If you omit the task name, you're prompted to choose one interactively. This fails in non-interactive mode (for example, in a CI pipeline), so always pass the task name explicitly there.

To pass run-time variables, use `--variable` in the format `<type>:<name>=<value>` (repeatable):

```bash theme={null}
upsun task:run myagent --variable env:BATCH_SIZE=100 --variable env:TARGET=prod
```

By default, the command returns as soon as the task starts. Add `--wait` to block until it completes:

```bash theme={null}
upsun task:run myagent --wait
```

To see the tasks defined on an environment, run `upsun task:list` (alias: `upsun tasks`):

```bash theme={null}
upsun task:list
```

This lists each task's name, type, command, and timeout.

See the [Upsun Cloud CLI reference](https://developer.upsun.com/cli/reference.md) for the full list of commands and options.

### From the API

To trigger a task through the API, see the [task run endpoint reference](https://developer.upsun.com/api-reference/task/post-projects-environments-tasks-run.md):

```bash theme={null}
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:

```bash theme={null}
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"variables": {"env": {"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](https://developer.upsun.com/api/rest/authentication.md) for the exchange process and [API tokens](https://developer.upsun.com/cli/api-tokens.md) 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**](https://developer.upsun.com/docs/configure-apps/image-properties/authorizations.md) (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](#sample-implementation).

### 1. Declare the authorization


&#x20;   applications:
&#x20;     myapp:
&#x20;       authorizations:
&#x20;         \- type: task
&#x20;           resource: myagent
&#x20;           action: operate

### 2. Request a token and trigger the task

```bash theme={null}
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.

```python title="Python" theme={null}
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={"env": {"BATCH_SIZE": "100", "TARGET": "prod"}})
```

```javascript title="Node.js" theme={null}
const cache = {};

async function getToken() {
  if (cache.expiresAt - 30_000 > Date.now()) return cache.token;
  const res = await fetch('http://localhost:8200/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'x-token-ttl': '900', // 60–900 seconds
    },
    body: 'grant_type=client_credentials',
  });
  const data = await res.json();
  cache.token = data.access_token;
  cache.expiresAt = Date.now() + (data.expires_in ?? 900) * 1000;
  return cache.token;
}

export async function triggerTask(taskName, variables = {}) {
  const token = await getToken();
  const { PLATFORM_PROJECT, PLATFORM_BRANCH } = process.env;
  const url = `https://api.upsun.com/projects/${PLATFORM_PROJECT}/environments/${PLATFORM_BRANCH}/tasks/${taskName}/run`;
  const hasVars = Object.keys(variables).length > 0;
  const res = await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      ...(hasVars ? { 'Content-Type': 'application/json' } : {}),
    },
    body: hasVars ? JSON.stringify({ variables }) : undefined,
  });
  return res.json();
}

// Example: pass run-time variables
await triggerTask('myagent', { env: { 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, or in the Console on the **Configure resources** page.
* **Billing**: Per-second for the duration of each run. CPU and memory cost the same as an application; see the [Upsun pricing page](https://upsun.com/pricing/) for rates. Usage is accounted for within the application's resources; there is no separate usage group for tasks on the billing page.
* **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](https://developer.upsun.com/docs/security/project-isolation.md) topic.

For tasks that run untrusted code (for example, an LLM-driven agent), consider pairing with [bubblewrap](https://github.com/containers/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. During a [trial](https://developer.upsun.com/docs/glossary.md#trial), this limit is lower.
* **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.** An Upsun Cloud project configuration requires at least one application container to run code, handle routing, and connect to managed services.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.