Skip to main content

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 Mastra
Direct 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:

  1. A parent Render task coordinates the run.
  2. Child Render tasks invoke Mastra agents for focused work.
  3. 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.

Setup
Direct 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
note

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 create mastra@latest render-workflows -- --empty
cd render-workflows

Install the Render SDK:

npm install @renderinc/sdk tsx

Set the API key for your model provider. This example uses OpenAI:

.env
OPENAI_API_KEY=your_openai_api_key

Any supported Mastra model provider works.

Build a distributed agent pipeline
Direct link to Build a distributed agent pipeline

Create the agents
Direct link to Create the agents

In src/mastra, create an agents directory with reviewer-agent.ts and editor-agent.ts. Define both agents:

src/mastra/agents/reviewer-agent.ts
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.
`,
})
src/mastra/agents/editor-agent.ts
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 instance
Direct link to Configure the Mastra instance

Add both agents to the Mastra instance in src/mastra/index.ts:

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 task
Direct 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.

src/tasks/review-task.ts
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 task
Direct 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.

src/tasks/revision-task.ts
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 task
Direct 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.

src/tasks/editorial-task.ts
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 tasks
Direct link to Register the tasks

Create src/index.ts and import the editorial task:

src/index.ts
import './tasks/editorial-task.js'

Running the entry point loads the module and registers every task defined with task().

Change your tsconfig.json:

tsconfig.json
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"noEmit": false
}
}

Add these scripts to package.json:

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 pipeline
Direct link to Run the pipeline

Local
Direct 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.

Production
Direct 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.

  1. Push the project to a Git repository
    Direct link to Push the project to a Git repository

    Render 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.

  2. Create the workflow service
    Direct link to Create the workflow service

    In the Render Dashboard, click New > Workflow and link the repository from the previous step. Then complete the creation form:

    FieldValue
    LanguageNode
    RegionThe region of any other Render services your tasks connect to
    Build Commandnpm install && npm run build
    Start Commandnpm run start:workflows

    Click Deploy Workflow. Render builds the project and registers review_draft, revise_draft, and editorial_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 the origin remote of your local repository, so the project must already be pushed.

  3. Set the workflow's environment variables
    Direct link to Set the workflow's environment variables

    Add OPENAI_API_KEY, or the key for your chosen model provider, to the workflow service in the Dashboard before the first run.

  4. Start a run
    Direct link to Start a run

    render 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 application
Direct 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:

.env
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:

src/start-editorial-pipeline.ts
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.