• Docs
  • Login
Talk to an expertTry for free
Blog
Blog
BlogProductCase studiesNewsInsights
Blog

Task containers: run-to-completion work on Upsun Cloud

AI Agentscontainersinfrastructure automationcost savings
Share

TL;DR

  • The primitive. A new container type for ephemeral, on-demand, run-to-completion work.
  • Lifecycle. Spins up when called, executes its command, and exits when done.
  • Configuration. Under a new tasks: key in .upsun/config.yaml, alongside applications: and services:.
  • Use cases. Background AI agents, batch jobs, report generation, and event-driven webhooks.
  • Access. Generally available on Upsun Cloud Flex plans.

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.

What is a task container?

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:

  • Ephemeral. It is created when triggered and removed when its command exits. Nothing keeps running between invocations.
  • On-demand. You start a task from the console, the CLI (upsun task:run), or the API: from CI, from a cron job, from another service, or from inside one of your apps.
  • Run-to-completion. The container has a clear job and a clear end. Timeouts, resource limits, and activity logging are built in.

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.

How tasks fit into a project

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.

What you can run on it

AI agents

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:

  • The agent runs inside a real environment, with access to the repository, the database, and any service the rest of your project uses.
  • Container-level isolation gives each agent run its own isolated container. For an LLM agent that executes model-generated commands, pair this with an in-container sandbox like bubblewrap to further restrict the filesystem and syscalls visible to the agent process.
  • Activity logging captures what the agent did, so there is a paper trail.
  • Workload authorizations, covered below, give the agent short-lived, narrowly-scoped tokens to call back into the Upsun Cloud API.

A few concrete agent shapes that fit cleanly:

  • A support agent triggered when a new ticket arrives, classifying it and drafting an initial response.
  • A performance agent that wakes up when a metric crosses a threshold.

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 jobs and data work

Batch is the broadest use case on the platform, and the one most Upsun Cloud customers will reach for first.

Examples:

  • A CSV import triggered when a user uploads a file.
  • A customer data export generated when an account holder requests their history.
  • A reindexing pass that rebuilds a search index after a schema change.
  • An image transformation pipeline that runs after a content upload.

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.

Heavy data operations

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.

Event-driven webhooks

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.

What you get out of the box

Every task comes with:

  • Its own container image and build step, independent from the rest of the project.
  • Per-second billing for the duration of each run, so an idle task costs nothing.
  • Timeouts and resource limits to stop runaway tasks. Default timeout is one hour; the maximum is one day.
  • Activity logging, so you can see what ran, when, and what it did.
  • Container isolation, with the same namespace and network controls as applications.
  • Access to the project services you link, through the standard relationships model.
  • Workload authorizations, the short-lived token mechanism described below.

Workload authorizations: short-lived tokens for runtime API calls

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.

Key considerations

  • Deploys stop running tasks. The platform sends a stop signal first, then forces termination after a short grace period. Design tasks to save progress as they go and to be safe to re-run.
  • Three tasks at once per project. That's the default cap. Anything triggered beyond it queues up and waits for an open slot.
  • Tasks don't connect to each other directly. If two parts of your project need to share data, route it through a service (a database, a cache, or shared storage) that both can reach.
  • Each task run starts with a clean filesystem. The default working directories are wiped between runs. To keep files across runs, mount a storage or service volume.
  • Every project needs at least one application. Tasks can't stand alone. A project with only tasks and services isn't valid.
  • Tasks appear in a Tasks card below Apps & Services on your project and environment overview, alongside status, last run, allocation, and relationships.
  • Tasks don't serve HTTP. They can't be used as a route upstream. If you need request and response handling, that's still an application's job.

Ready to build one? Head to the task containers documentation to get started.


Frequently asked questions (FAQ)

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.

Stay updated

Subscribe to our monthly newsletter for the latest updates and news.

Your greatest work
is just on the horizon

Free trial