Render
Deploy Mastra applications on Render. Host the Mastra API as a web service, or use Render Workflows for long-running tasks with independent retry policies.
Choose the deployment path that fits your application:
- Mastra API: Deploy Mastra's server as a web service with a public endpoint. The Web Services guide explains how to deploy custom code or a supported server adapter.
- Mastra workflow: Run an entire Mastra workflow within one task. Render controls the outer run, while Mastra manages its steps and state. See Defining Workflow Tasks for configuration details.
- Distributed agent operations: Give each operation its own compute plan, timeout, and retry policy. Render Workflows handles the execution queue and provides run observability.
This guide builds an editorial pipeline that reviews a draft from three perspectives in parallel, then passes the feedback to an editor agent. Use the links above if you want to deploy a Mastra API or execute an entire Mastra workflow as one task.
How Render Workflows integrate with MastraDirect link to How Render Workflows integrate with Mastra
Mastra supplies the agents and application logic, while Render Workflows defines the execution boundaries. A typical pipeline has three layers:
- A parent Render task coordinates the run.
- Child Render tasks invoke Mastra agents for focused work.
- A final Render task combines the results.
Calling one Render task from another creates a chained task run. Each chained run executes in its own instance. You can set compute, timeout, and retries on that task alone.
This guide builds an editorial pipeline that reviews a draft from three perspectives in parallel, then uses a Mastra editor agent to produce a revised version. You can try a running copy in the live demo.
SetupDirect link to Setup
You need a Render account and the Render CLI. The CLI runs a local task server during development. You can also use it to create a workflow service on Render.
Install the CLI and log in:
brew install render
render login
Requires Render CLI v2.12.0 or later. For other installation methods, see the Render CLI docs.
Create an empty Mastra project named render-workflows:
- npm
- pnpm
- Yarn
- Bun
npm create mastra@latest render-workflows -- --empty
pnpm create mastra render-workflows --empty
yarn create mastra render-workflows --empty
bunx create-mastra render-workflows --empty
cd render-workflows
Install the Render SDK:
- npm
- pnpm
- Yarn
- Bun
npm install @renderinc/sdk tsx
pnpm add @renderinc/sdk tsx
yarn add @renderinc/sdk tsx
bun add @renderinc/sdk tsx
Set the API key for your model provider. This example uses OpenAI:
OPENAI_API_KEY=your_openai_api_key
Any supported Mastra model provider works.
Build a distributed agent pipelineDirect link to Build a distributed agent pipeline
Create the agentsDirect link to Create the agents
In src/mastra, create an agents directory with reviewer-agent.ts and editor-agent.ts. Define both agents:
import { Agent } from '@mastra/core/agent'
export const reviewerAgent = new Agent({
id: 'reviewer-agent',
name: 'Reviewer Agent',
model: 'openai/gpt-5.6-sol',
instructions: `
Review the supplied draft only from the requested perspective.
Identify concrete problems and recommend specific changes.
Do not rewrite the full draft.
`,
})
import { Agent } from '@mastra/core/agent'
export const editorAgent = new Agent({
id: 'editor-agent',
name: 'Editor Agent',
model: 'openai/gpt-5.6-sol',
instructions: `
Revise the supplied draft using the reviewers' feedback.
Return the complete revised draft and nothing else.
Preserve accurate details and do not introduce unsupported claims.
`,
})
Configure the Mastra instanceDirect link to Configure the Mastra instance
Add both agents to the Mastra instance in src/mastra/index.ts:
import { Mastra } from '@mastra/core/mastra'
import { editorAgent } from './agents/editor-agent.js'
import { reviewerAgent } from './agents/reviewer-agent.js'
export const mastra = new Mastra({
agents: {
editorAgent,
reviewerAgent,
},
})
Retrieving agents from the Mastra instance gives them access to shared application services such as logging, storage, and observability.
Create the review taskDirect link to Create the review task
In src, create a tasks directory. The reviewer agent handles one area of focus. Its compute plan, five-minute timeout, and retry policy apply only to that analysis. A temporary model-provider failure can trigger another attempt without restarting the other reviewers.
import { task } from '@renderinc/sdk/workflows'
import { mastra } from '../mastra/index.js'
type Review = {
focus: string
feedback: string
}
export const reviewDraft = task(
{
name: 'review_draft',
plan: 'starter',
timeoutSeconds: 300,
retry: {
maxRetries: 2,
waitDurationMs: 1_000,
backoffScaling: 2,
},
},
async function reviewDraft(draft: string, focus: string): Promise<Review> {
const reviewer = mastra.getAgentById('reviewer-agent')
const response = await reviewer.generate(`
Review this draft for ${focus}.
Draft: ${draft}
`)
if (!response.text) {
throw new Error(`The ${focus} review returned no text`)
}
return {
focus,
feedback: response.text,
}
},
)
Create the revision taskDirect link to Create the revision task
This task combines the feedback and produces a revised draft. It uses a larger compute plan and a longer timeout than each reviewer.
import { task } from '@renderinc/sdk/workflows'
import { mastra } from '../mastra/index.js'
type Review = {
focus: string
feedback: string
}
export const reviseDraft = task(
{
name: 'revise_draft',
plan: 'standard',
timeoutSeconds: 600,
retry: {
maxRetries: 2,
waitDurationMs: 1_000,
backoffScaling: 2,
},
},
async function reviseDraft(draft: string, reviews: Review[]): Promise<{ draft: string }> {
const editor = mastra.getAgentById('editor-agent')
const response = await editor.generate(`
Revise the draft using the review feedback.
Draft: ${draft}
Reviews: ${JSON.stringify(reviews, null, 2)}
`)
if (!response.text) {
throw new Error('The editor returned no text')
}
return {
draft: response.text,
}
},
)
Create the orchestration taskDirect link to Create the orchestration task
The parent dispatches three reviews in parallel with Promise.all(), then sends their combined feedback to the revision step. Retries are disabled at this level because each child defines its own policy. If you enable orchestration retries, ensure that another attempt cannot duplicate external side effects or other non-idempotent work.
import { task } from '@renderinc/sdk/workflows'
import { reviewDraft } from './review-task.js'
import { reviseDraft } from './revision-task.js'
export const editorialPipeline = task(
{
name: 'editorial_pipeline',
plan: 'starter',
timeoutSeconds: 1_200,
retry: {
maxRetries: 0,
waitDurationMs: 1_000,
backoffScaling: 2,
},
},
async function editorialPipeline(draft: string): Promise<{ draft: string }> {
const focuses = ['technical clarity', 'structure and flow', 'reader usefulness']
const reviews = await Promise.all(focuses.map(focus => reviewDraft(draft, focus)))
return reviseDraft(draft, reviews)
},
)
Register the tasksDirect link to Register the tasks
Create src/index.ts and import the editorial task:
import './tasks/editorial-task.js'
Running the entry point loads the module and registers every task defined with task().
Change your tsconfig.json:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"noEmit": false
}
}
Add these scripts to package.json:
{
"scripts": {
"build": "tsc",
"dev:workflows": "tsx src/index.ts",
"start:workflows": "node dist/index.js"
}
}
Those files are the complete pipeline. render-examples/render-workflows-mastra mirrors this src/ layout and adds a web UI that starts the parent task.
Run the pipelineDirect link to Run the pipeline
LocalDirect link to Local
Start the local Render Workflows development server:
render workflows dev -- "npm run dev:workflows"
In another terminal, list the registered tasks:
render workflows tasks list --local
Start the editorial pipeline:
render workflows tasks start editorial_pipeline \
--local \
--input='["Render Workflows runs long-running tasks outside the request lifecycle."]'
The local server keeps runs and their logs in memory, so you can inspect them after they finish with render workflows runs list <task-name> --local.
ProductionDirect link to Production
Running the pipeline on Render requires a workflow service. This is the Render service that holds your task definitions: it builds your repository, registers every task it finds, and provisions an instance for each run.
Push the project to a Git repository
Direct link to Push the project to a Git repositoryRender builds workflow services from a repository on GitHub, GitLab, or Bitbucket, so push your project to one of those providers. The first time you use a provider, Render asks for permission to access your repositories.
Create the workflow service
Direct link to Create the workflow serviceIn the Render Dashboard, click New > Workflow and link the repository from the previous step. Then complete the creation form:
Field Value Language Node Region The region of any other Render services your tasks connect to Build Command npm install && npm run buildStart Command npm run start:workflowsClick Deploy Workflow. Render builds the project and registers
review_draft,revise_draft, andeditorial_pipeline, which then appear on the workflow's Tasks page.The Render CLI creates the same service without leaving your terminal:
render workflows create \--name mastra-workflows \--repo . \--runtime node \--build-command "npm install && npm run build" \--run-command "npm run start:workflows"--repo .reads theoriginremote of your local repository, so the project must already be pushed.Set the workflow's environment variables
Direct link to Set the workflow's environment variablesAdd
OPENAI_API_KEY, or the key for your chosen model provider, to the workflow service in the Dashboard before the first run.Start a run
Direct link to Start a runrender workflows tasks start mastra-workflows/editorial_pipeline \--input='["Render Workflows runs long-running tasks outside the request lifecycle."]'The first part of that identifier is your workflow's slug and the second is the task name. Both appear on the task's page in the Render Dashboard, so use the slug shown there if your workflow has a different name.
Open the workflow in the Render Dashboard to inspect each task run, attempt, result, and log stream.
Trigger from your applicationDirect link to Trigger from your application
You can trigger the pipeline asynchronously from a Mastra application, web service, or script with the Render SDK.
Triggering runs from code requires a Render API key, which you create in your Render account settings. Set it in the calling service:
RENDER_API_KEY=rnd_your_api_key
The SDK reads RENDER_API_KEY from the environment automatically.
Start the task and return its run ID immediately:
import { Render } from '@renderinc/sdk'
const render = new Render()
export async function startEditorialPipeline(draft: string) {
const run = await render.workflows.startTask('mastra-workflows/editorial_pipeline', [draft])
return {
taskRunId: run.taskRunId,
}
}
The task continues running after startTask() returns. Call await run.get() when the caller should wait for the completed result instead.
Returning the task run ID from a request handler lets the application respond without keeping the request open for the full pipeline.