
TL;DR
|
Task containers are now generally available on Upsun Cloud, giving you a place to run AI agents alongside the rest of your application. The agent runs as a container inside your Upsun Cloud project, next to the app it works on, with the same access to your environment and data as everything else deployed there. The lifecycle is the part that's new: it fits how agent workloads behave instead of forcing them to pretend to be servers.
A few things up front:
This is a hands-on guide. If you want the broader feature introduction, read Task containers: run-to-completion work on Upsun first.
The task container is a clean execution primitive, by design. There's no AI gateway, no managed LLM, no orchestration UI layered on top, you bring the agent code, the LLM keys, and the trigger. That's a deliberate choice: a flexible execution environment stays useful no matter which model, framework, or agent pattern you're using, rather than locking you into whatever we'd have built in.
Before you start
You will need:
If you do not yet have an answer to the trigger question, that is the right place to start. An AI agent without a clearly scoped trigger is a process looking for an excuse to run; an agent with a precise trigger is a piece of infrastructure.
An agent can run in an app container or a worker, but a task container usually offers the better execution model. Define the agent as a task, and it comes with:
tasks: in .upsun/config.yaml, on par with applications: and services:.The mental model worth holding on to: an agent is a piece of code with a job to do, not a server. Treat it like a one-shot job that happens to call an LLM, and most of the design questions fall into place.
To make this concrete, the rest of the post walks through a support ticket triage agent. The scenario:
When a new ticket comes in, run an agent that reads the ticket content, classifies it against your support categories, drafts a response, and posts it back to the ticket for a human to review or send.
This same pattern generalizes. Swap "ticket triage" for "code review," "incident summary," or "data quality check," and the structure barely changes.
In .upsun/config.yaml, add a tasks: block at the top level. The task gets a name, a container image, a build step, and a command. The exact field reference lives in the task containers documentation; the conceptual structure is:
applications:
api:
# your long-running API
services:
postgres:
type: postgresql:16
tasks:
ticket-triage-agent:
type: python:3.14
run:
command: python agent.py
Three decisions matter here. Pick the minimum base image your agent needs. A Python agent using a small LLM client library doesn't need a heavyweight image, and a faster start means a faster end-to-end response. Install your agent's dependencies during the build step rather than at runtime, so the task is ready to execute the moment it starts. And keep the run command a single, idempotent script. If the agent retries, it should retry cleanly, without redoing work or leaving anything half-finished.
Tasks on Upsun Cloud are triggered through the platform API, the Console, or the CLI. For the ticket triage scenario, the trigger is a webhook from your ticketing system firing when a new ticket is created.
There are two common patterns for taking a webhook and turning it into a task invocation. Your existing API can receive the GitHub webhook directly, validate the signature, extract the relevant context (PR number, repo, base, and head SHAs), and call the Upsun Cloud API to invoke the task with those values as inputs. Or a dedicated lightweight handler can exist purely to receive webhooks and invoke tasks, useful if you want to keep the main application free of agent-related code paths.
For a ticket triage agent, the first pattern is usually enough. The handler is twenty lines of code; the agent itself is where the interesting work happens.
If your trigger is a schedule rather than an event (a nightly cleanup agent, for example), the same task can be invoked from a cron app or an external scheduler. The agent does not know or care which one called it.
The agent will need credentials, and there are two kinds that should be handled differently.
External credentials, like your LLM provider API key and a GitHub token, are stored as Upsun Cloud project variables. They are available to the task at runtime as environment variables. Treat them like any other secret: scoped to the project, rotated on a cadence, never committed to the repository.
Upsun Cloud platform credentials, for any case where the agent needs to call the Upsun Cloud API itself, are what workload authorizations are for. The task asks Upsun Cloud for a short-lived, narrowly-scoped token at runtime, uses the token, and lets it expire. There is no long-lived platform credential to rotate and no shared secret to leak in logs.
For a pure ticket triage agent that just reads a ticket and posts a reply, you may not need workload authorizations at all. They become useful as soon as the agent needs platform-level information or composition: querying Upsun Cloud for environment metadata, triggering a follow-on task, or labeling outputs with branch context. The workload authorization documentation covers the request flow.
This is the step most agent tutorials skip. Do not skip it.
Give the agent a hard timeout. The default is one hour, but for a ticket triage agent you'll want it lower. If a run hasn't finished in five minutes, something is wrong, and the right outcome is to fail the task and log it rather than let it keep going. Without a timeout, a runaway loop or a hung LLM call will consume the container until something else intervenes.
Set the container's CPU and memory allocation to match the actual work. An agent doing LLM-mediated reasoning is mostly waiting on the model API, so it rarely needs a large allocation.
Agents execute model-generated commands, and right now, task containers don't offer egress scoping, so a task's outbound network access isn't something you can restrict to specific endpoints yet. Until that's available, treat the credentials the agent holds as the real boundary: give it only the access it needs, since the container itself won't limit where it can reach. Pair this with an in-container sandbox like bubblewrap for tighter control over what the agent process can actually do.
Each task run gets its own isolated container, with the same namespace and network isolation as your applications. For an agent executing model-generated commands, pair this with an in-container sandbox like bubblewrap to further restrict the filesystem and syscalls visible to the agent process. Nothing persists between runs by default, so anything that needs to survive should go to a service (Postgres, object storage), not local disk.
One more limit worth planning around: Upsun Cloud runs up to three tasks concurrently per project by default. If your repository sees a burst of pull requests, additional triggers queue for an open slot rather than failing outright. That's usually fine for a ticket triage agent, since a few minutes' delay on a reply rarely matters, but it's worth knowing before a busy afternoon makes you think something's broken.
Getting these right is what separates an agent you can run unattended from one you have to babysit.
Activity logging is built in. The task records what ran, when, and with what status. That covers the operational view.
What it does not cover is the agent-internal view: which prompts the agent generated, which tools it called, which model responses it received, and what it decided to do. For that, instrument the agent yourself. At minimum, log the input the agent received (PR number, file paths, commit SHAs), each LLM call (the model, a hash of the prompt, the response length, and the cost if you track it), each external action the agent took (API call, comment posted, file written), and the final outcome.
Stream these to whatever observability tooling you already use. The point is to make agent runs reconstructable after the fact, because LLM behavior changes, and you will want to know what an agent actually did three weeks from now.
With the task defined, the trigger wired, credentials in place, and limits set, open a pull request and let the agent run. Watch the activity log. Read the comment it posts. Adjust the prompt, the limits, the trigger conditions as you learn.
The first ten runs are the most valuable. They tell you whether your scoping is right, whether the agent is reaching for things it should not, and whether the prompt is doing what you expect on real data. Treat the first batch as a calibration phase, not a launch.
The walkthrough above generalizes. A few other agent patterns that fit the task container cleanly:
In each case the shape is the same: a trigger arrives, the agent runs to completion, the container is removed. What changes is the prompt, the tools, and the destination of the output.
The task container ships the runtime; you bring the agent: your own framework, LLM connection, and prompt management. That's deliberate. Agent frameworks and orchestration patterns shift month over month, and a primitive that doesn't lock you in ages better than one that does.
Ready to begin? Head to the task containers documentation on developer.upsun.com to get started.
Do I have to use a specific agent framework?
No. The task container is framework-agnostic. Use LangChain, LlamaIndex, AutoGen, the OpenAI or Anthropic SDKs directly, or your own code.
Can the agent reach other parts of my Upsun Cloud project?
Yes. Tasks have access to the project's services (databases, caches, queues, object storage) through standard relationships, the same way an app or worker does.
How does the agent talk back to the Upsun Cloud API?
With workload authorizations. The task requests a short-lived, narrowly-scoped token at runtime and uses it to call the API. There are no long-lived platform credentials in the agent's environment.
What happens if the agent runs longer than the timeout?
The task is terminated, and the failure is recorded in the activity log. Default timeout is one hour; the maximum is one day. Set the timeout to a value that comfortably covers the slowest legitimate run but stops runaway loops.
Can I run multiple agents in the same project?
Yes. Each agent is its own entry under tasks:, with its own image, build step, and command.
How many agent runs can happen at once?
Three by default, per project. Additional triggers queue until a slot opens rather than failing.
Where can I see what the agent actually did?
Activity logs show task-level status and streamed logs. Agent-internal decisions (prompts, tool calls, model responses) need to be logged from inside the agent code. Both views matter.