
TL;DR
|
Most of what runs on a modern platform is built to stay up. Applications serve requests, workers process queues, and managed services keep databases and caches available around the clock. But a meaningful share of the real work does not fit that always-on shape.
A bulk import triggered when a user uploads a file, a one-off data fix triggered from an admin tool, an AI agent triaging a stack of pull requests, a webhook handler reacting to a single Stripe event: each of these has a start, a job to do, and a finish line, and forcing any of them into a long-running process is a workaround.
The task container is Upsun Cloud's infrastructure primitive built for run-to-completion work.
A task is an on-demand, run-to-completion workload defined alongside your applications and services. Three things make it different from an app or a worker:
upsun task:run), or the API: from CI, from a cron job, from another service, or from inside one of your apps.Tasks are configured in the same file as the rest of your project. They have their own container image and their own build step, independent from your application. They reach the rest of the environment, including databases, caches, and any other services, through the standard Upsun Cloud relationships model.
An Upsun Cloud project has long been built out of two primitives: applications, which serve requests and can include background workers, and services, the managed components like databases and caches.
tasks: joins applications: and services: at the top level of .upsun/config.yaml:
applications:
api:
# your long-running API
services:
postgres:
type: postgresql:18
tasks:
myagent:
type: nodejs:24
run:
command: node agent.js
See the configuration reference in the task containers documentation.
Agent workloads have a specific shape. They need an environment with access to code and data. They need isolation, because they execute model-generated commands. They need scoped permissions because you do not want an agent reaching wherever it wants on the platform. And they need to stop running when the work is done, so you are not paying for an idle container between runs.
That last part is the gap the task container closes for AI on Upsun. The rest was already there:
A few concrete agent shapes that fit cleanly:
This is a deliberately clean execution primitive, by design. You bring your own agent code, your own LLM provider and keys, and your own framework or SDK, so you're never locked into a specific model, language, or agent pattern. We give you a flexible execution environment; you use it however your application needs.
Batch is the broadest use case on the platform, and the one most Upsun Cloud customers will reach for first.
Examples:
Each of these has the same shape: triggered, runs, finishes. The question is where that work runs.
The default is to run it inside the app container itself, and that's fine as long as the job is light. But it shares the same resources serving your app's traffic, so anything resource-intensive competes with live requests and can hurt performance.
The traditional fix has been to move that work into a worker. That solves the resource-contention problem, but trades it for a different one: a worker sits idle between runs, so you're paying for that idle time regardless of how often the work actually happens.
Cron jobs run into the same tension. They also execute in the app container, so a resource-intensive cron job competes with live traffic the same way an ad hoc job would. Scheduling it for the middle of the night often works around this, since there's no traffic to compete with. But if your app sees more even, international traffic, or the job needs to run more often than once a day, that quiet window may not exist. A cron job can trigger a task instead, so the heavy lifting happens in its own container rather than pulling from your app's resources.
Routine schema migrations tied to a code change belong in your deploy hook, that's the natural place for them, and a task container doesn't change that.
Where a task container helps is with the heavier, less routine work: a one-off data migration, a large data transformation, or a bulk import too resource-intensive to run inline. With a task container, that step is a first-class part of the project configuration, not a Slack thread describing what someone did by hand.
External webhooks usually reach task containers indirectly. Stripe sends a payment event. GitHub sends a push event. Slack sends an interaction. Most commonly, these arrive at an always-on application that validates the payload and then calls the Upsun Cloud API to trigger a task. The app stays light and responsive; the task does the long-running work and exits when done.
If the webhook source can hold an authenticated token itself, it can call the API directly to trigger the task, no app in the middle needed.
Every task comes with:
Shipped alongside the task container, workload authorizations let an application or task call the Upsun Cloud API at runtime using short-lived, narrowly-scoped tokens. There are no long-lived credentials to store and no rotation to manage.
This is useful well beyond agents. Any workload that needs to talk to the platform API benefits: an app that triggers a heavy data import task when a user uploads a file, an internal admin dashboard that lists active environments and their status, or a task that reads environment metadata to tag its outputs with the branch name. The pattern is the same: ask the platform for a token, use it, let it expire.
The workload authorizations section provides further details.
Ready to build one? Head to the task containers documentation to get started.
Is a task container the same as a serverless function?
The execution model is similar: triggered, run, exit. The difference is that a task runs inside your Upsun environment, with direct access to your services and data through relationships. You are not making a network hop to a separate platform to reach your database.
Can I run scheduled tasks?
A task is triggered on demand, through the console, CLI, or API. Scheduling is something you wire up on top, with a cron app, a CI job, or an external scheduler.
What happens if a task fails or hits its timeout?
The run is recorded as failed in the activity log. There is no automatic retry; whatever triggered the task is responsible for retrying if needed. Default timeout is one hour, with a one-day maximum. Design tasks to be idempotent so a retry is safe.
What runtimes can I use for a task?
The same runtime images as applications. Tasks declare a type: in .upsun/config.yaml, exactly like apps do: Node.js, Python, PHP, Ruby, Go, Java, .NET. For AI agents, the choice is whatever your framework or SDK needs.
How is a task different from a worker?
A worker is a long-running process that starts on deploy and stays up to poll a queue or do background work. A task is on-demand: it starts when triggered, runs once, and exits. If work arrives constantly, use a worker. If work arrives in bursts or comes from discrete events, use a task.
Do tasks share storage with my application?
Tasks have their own container image and a fresh filesystem each run. They share persistent state with apps the same way apps share it with each other: through services (databases, object storage, caches) using the standard relationships model. To persist files across task runs, mount a storage or service volume. The default instance and tmp mounts reset between runs.
Can a task call the Upsun Cloud API?
Yes. That is what workload authorizations are for: a task asks the platform for a short-lived, narrowly-scoped token and uses it to call the API.