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 integrates with MastraDirect link to How Render Workflows integrates with Mastra
Mastra supplies the agents and application logic, while Render Workflows defines the execution boundaries. A typical pipeline has three layers:
- A parent task coordinates the run.
- Child tasks invoke Mastra agents for focused work.
- A final task combines the results.
Calling one task from another creates a chained run in a separate instance, with its own compute plan, timeout, and retry policy.
SetupDirect link to Setup
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
Install two additional dependencies:
- npm
- pnpm
- Yarn
- Bun
npm install @renderinc/sdk tsx
pnpm add @renderinc/sdk tsx
yarn add @renderinc/sdk tsx
bun add @renderinc/sdk tsx
Add your API key to an .env file. This example uses OpenAI, but any supported model provider works.
OPENAI_API_KEY=your_openai_api_key
Install the Render CLI to run workflow commands from your terminal.
Distributed agent pipelineDirect link to 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,
},
})
Create the tasksDirect link to Create the tasks
In src, create a tasks directory with review-task.ts, revision-task.ts, and editorial-task.ts.
Review taskDirect link to Review task
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,
}
},
)
Revision taskDirect link to 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,
}
},
)
Editorial taskDirect link to Editorial 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 can't 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)
},
)
Set up the entry pointDirect link to Set up the entry point
Create src/index.ts and import the editorial task:
import './tasks/editorial-task.js'
In package.json, add scripts to build the TypeScript project and run the workflow:
{
"scripts": {
"build": "tsc",
"dev:workflows": "tsx src/index.ts",
"start:workflows": "node dist/index.js"
}
}
Configure tsconfig.json for the build:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Build the project. The command compiles the JavaScript files into dist.
- npm
- pnpm
- Yarn
- Bun
npm run build
pnpm run build
yarn build
bun run build
Run the pipelineDirect link to Run the pipeline
LocallyDirect link to Locally
Start the local development server with the Render CLI:
render workflows dev -- npm run dev:workflows
The server lists the registered tasks:
➜ render workflows dev -- npm run dev:workflows
Workflow server listening on port 8120
Loaded environment variables from .env
3 tasks found in npm run dev:workflows
• editorial_pipeline
• review_draft
• revise_draft
To browse and run tasks, open another terminal and run:
render workflows tasks list --local
In another terminal, confirm that the tasks are available:
render workflows tasks list --local
The command displays each task's name, ID, and creation time. Press Ctrl+C, then start the editorial pipeline from the same terminal:
render workflows tasks runs start editorial_pipeline \
--local \
--input='["Render Workflows runs long-running tasks outside the request lifecycle."]'
The development server records the parent run, three parallel reviews, and the final revision.
ProductionDirect link to Production
Create a workflow service from the current repository:
render workflows create \
--name mastra-workflows \
--repo . \
--runtime node \
--build-command "npm install && npm run build" \
--run-command "npm run start:workflows"
Add OPENAI_API_KEY, or the key for your chosen model provider, to the service's environment variables.
After deployment, start a production run. If needed, replace mastra-workflows/editorial_pipeline with the task slug shown in the Render Dashboard.
render workflows tasks start mastra-workflows/editorial_pipeline \
--input='["Render Workflows runs long-running tasks outside the request lifecycle."]'
Open the workflow service in the Render Dashboard to inspect each task run, attempt, result, and log stream.