Migrating production infrastructure? Get up to $10K in migration credits.

Apply now

Deploy a Next.js app with Postgres and Render Workflows using Cursor

You'll end up with a Next.js app and a Render Postgres database deployed from a render.yaml file that Cursor writes for you. A long-running job runs as a Render Workflows task instead of inside a request. The agent's first pass usually gets you most of the way there, but a few Next.js-specific details tend to slip through: service type, build-time variables, port binding, migrations, and connection pooling. This guide gives you the prompt for each step and the follow-up prompt for each common miss, so you can correct the agent without writing the config yourself.

Install the Render plugin in Cursor

Install the Render plugin from the Cursor marketplace. For setup details, see render.com/agents. This guide doesn't repeat them.

The prompts below rely on the plugin. It lets the agent write Render config such as render.yaml and work with your Render resources from the chat.

Prompt the agent to write render.yaml

Open your Next.js repo in Cursor. Start with a prompt that states the facts the agent can't reliably infer. For example, you might write:

When the agent shows you the file, review it against the table below before you move on. Each row names what to review and what a correct output contains. For field details, see the Blueprint specification.

What to review
What a correct output contains
Next.js service
A web service, which is a dynamic web app with a public onrender.com subdomain for receiving HTTP traffic. Static site is a separate service type. Accept it only if your app is a fully static export.
Postgres database
A Render Postgres database in the same file. If the agent omits the version, Render uses the most recent version the platform supports. Pin a version only if your ORM or extensions need one.
DATABASE_URL
A reference to the database's connection string, not a pasted value. Services in the same region as the database can always use its internal URL.
Generated secrets
Values such as session secrets set to generate automatically, which produces a random, base64-encoded, 256-bit value. Reject any secret the agent copied from your local .env.
Root directory
Set only if the Next.js app lives in a monorepo subfolder. Otherwise Render uses the repo root.

The prompt states the service type explicitly because agents often guess from the presence of next build, and that guess can go either way. For the full reasoning on when SSR and API routes require a web service, see Render's article on deploying Next.js applications with SSR and API routes.

If you plan to use preview environments, read the Workflows preview limitation in Workflows limits to plan around before you add workflow config to this file.

Validate, deploy, and read the logs

Use this loop for every change, including the fixes in the next section:

  1. Ask the agent to validate render.yaml.
  2. Deploy.
  3. Have the agent read the build logs and runtime logs.
  4. Paste or summarize the errors into a follow-up prompt.

A prompt that covers the whole loop looks like this:

Asking for a summary before fixes keeps the agent from stacking speculative changes on top of each other. Most first-pass problems show up in the logs. For example, if Render can't detect a bound port, the deploy fails and the error appears in your logs. If a new deploy fails or its health checks don't pass within 15 minutes, Render cancels it and keeps routing traffic to your existing instances. Render runs the pre-deploy command after the build and before the start command, and if any command fails or times out, the entire deploy fails. Keep each migration backward compatible: the previous version keeps serving traffic on the migrated schema until the new deploy goes live, and keeps serving it if the deploy fails. The next section covers the failures you're most likely to see.

Fix what the agent gets wrong on first pass

Wrong service type

Symptom: Server-rendered pages or API routes don't behave the way they do locally. Or a static export runs as a web service it doesn't need.

Why the agent missed it: It inferred the type from build scripts instead of from how the app renders.

NEXT_PUBLIC_ variables empty in the browser

Symptom: Client-side code reads undefined for values that work fine in server code.

Why the agent missed it: It treated every variable as a runtime setting. Next.js writes NEXT_PUBLIC_ values into the client bundle at build time, so you need to set them before the build runs. For the details, see the Next.js environment variables documentation.

Port binding failures

Symptom: The deploy fails with a port detection error, or it times out waiting for health checks.

Why the agent missed it: It kept a start command that hardcodes port 3000 or binds to localhost. Every Render web service must bind to a port on host 0.0.0.0. Render recommends binding to the PORT environment variable, which defaults to 10000. Render reserves ports 18012, 18013, and 19099, so you can't use them. For more detail, see port binding.

Migrations that never ran

Symptom: Runtime logs show "relation does not exist" or missing-column errors right after a deploy.

Why the agent missed it: It added a build and start command but nothing for schema changes. Render supports a pre-deploy command, which has a 30-minute timeout. For more detail, see deploys.

If your web service runs on a Free instance, Render's docs say to run database migrations in the build command instead. For an example, see deploying a Rails app. Use this variant of the prompt:

No connection pooling

Symptom: Under load, logs show errors about too many connections.

Why the agent missed it: It wired the app straight to Postgres. That works until connection churn from bursty traffic or background jobs exceeds your plan's limit. Connection limits depend on the database's compute plan. Current-generation plans range from 100 connections on the smallest plan to 500 on plans with 32 GB of memory or more. Render Postgres offers integrated connection pooling with PgBouncer at no additional cost. Pooling helps most when bursty workloads such as Render Workflows open surges of short-lived connections. Pooling isn't available for free databases.

After you enable pooling on the database, update your clients to connect to PgBouncer instead of connecting directly to the database. Render's PgBouncer uses transaction-level pooling. Clients that use session-level advisory locks, LISTEN/NOTIFY, temporary tables, or custom session variables should connect directly. Render Postgres reserves 10 connections for direct connections and internal operations. Migration tooling often relies on session-level features such as advisory locks, so give it the direct connection. For more detail, see connection pooling.

This simplified example illustrates the split. Adapt it to the ORM you use. Check which Prisma major version your project runs (for example, with npx prisma --version), because the configuration differs. The first block is the Prisma 6 and earlier form:

Per Prisma's documentation, url and directUrl in the schema's datasource block are deprecated in Prisma 7, and the Prisma CLI runs migrations against the connection string in datasource.url in prisma.config.ts. Prisma 7 also requires a driver adapter to create the client. In Prisma 7, point the config file at the direct URL for migrations and have the runtime client connect with the pooled DATABASE_URL:

Run background work as a Render Workflows task

Render Workflows is an orchestration and execution engine for long-running, distributed tasks that you define as functions with Render's SDK. Each task run can execute for up to 24 hours. By default, task runs time out after 2 hours. In practice, your Next.js route handler can hand off slow work and respond immediately. You don't need a separate worker service or queue. Render handles task queuing, spin-up, and deprovisioning at no additional cost.

Prompt the agent to use the render-workflows skill

Name the job and the skill explicitly. If the render-workflows skill isn't already available to the agent after plugin setup, install Render's agent skills through the Render CLI, which requires CLI version 2.10 or later. For more detail, see LLM support.

Define the task and trigger it from Next.js

The task definition imports from the SDK package, and the SDK must be version ^1.0 or later. This minimal example shows the task registration the agent's file should contain. Compare the skill's output against it:

After you deploy the workflow, you trigger runs of its tasks from your app with the Render SDK or API. Each run executes in its own instance, which Render spins up on demand. If you trigger more runs than your compute limits allow, Render queues the excess.

A task run starts from that trigger, which passes its input as arguments. Before you design the task, read the scheduling and incoming-connection limits in Workflows limits to plan around.

The route handler imports its trigger from lib/render-workflows.ts. This simplified helper creates the SDK client, which reads RENDER_API_KEY from the environment automatically. It starts a run by task slug, which uses the format {workflow-slug}/{task-name}. Make sure @renderinc/sdk is at version ^1.0 or later:

This simplified route handler illustrates the thin-handler shape:

Review the agent's output

Before you deploy, confirm the following:

  • The task code lives in the directory the skill created, and lib/render-workflows.ts holds the SDK trigger call.
  • The trigger passes IDs, not full records or files.
  • The task doesn't open a server or wait for inbound requests. See Workflows limits to plan around.
  • Workflow config in render.yaml doesn't depend on preview environments, because Render skips preview copies of workflows that your Blueprint defines.

To test before you deploy, ask the agent to start the local task server, which listens on port 8120. The local task server requires Render CLI 2.12.0 or later, and it doesn't support workflows that build from a Dockerfile. For details, see local development with Render Workflows. For thin handlers, idempotency, and payload size, see Render's article on triggering a long-running task from a web service.

Add the workflow to render.yaml and connect Next.js to it

Your route handler can't start runs until the workflow service exists. Define it in the same render.yaml as your web service and database, so it's one of the workflows your Blueprint defines. Blueprints support workflow services with type: workflow. For TypeScript, the workflow uses runtime: node with a build command and a start command that runs your workflow entrypoint. All of your workflow's task runs execute in the region you pick, and that region determines which of your other Render services they can reach over your private network. Pick the same region as your database.

As an alternative, you can create the workflow in the Render Dashboard with New > Workflow, link the Git provider repo that holds your task definitions, and choose Node as the language for TypeScript. For that walkthrough, see Your first workflow. This guide keeps the workflow in render.yaml so all of your services live in one Blueprint.

Ask the agent to add the workflow and wire up the Next.js service:

After both services deploy, confirm the following:

  • The workflow service is defined in render.yaml, its deploy succeeded, and its region matches your database's.
  • The workflow service has DATABASE_URL set from the database, not a pasted value.
  • RENDER_API_KEY is declared with sync: false, and its value is set as a secret outside render.yaml, never in the repo.
  • REPORT_WORKFLOW_SLUG matches the workflow's slug, so lib/render-workflows.ts starts {workflow-slug}/generateReport.
  • A request to your route handler returns 202, and a new run of the task appears for the workflow. For more on starting runs, see running tasks.

Workflows limits to plan around

Plan around these limits before Workflows carries production traffic. The three limitations below come from the Render Workflows overview. For the numeric limits in the table, see the Workflows limits.

  • Scheduling: Render Workflows has no native scheduling. Runs start when you trigger them with the SDK or API. For recurring work, have a cron job trigger the task. At most one run of a given cron job is active at a time.
  • Incoming connections: Task runs can't receive incoming connections. Each run receives its input from the trigger and executes in its own on-demand instance. Don't design tasks to accept inbound requests.
  • Preview environments: Preview environments don't currently support Render Workflows. Render skips preview copies of workflows your Blueprint defines.
Limit
Value
Default task run timeout
2 hours
Maximum per-task timeout
24 hours
Chained runs per workspace
1,000 per minute
Task state retention
30 days

Where to go next

  • If a job needs to run on a schedule, add a cron job that triggers the task, as described in Workflows limits to plan around.
  • Turn on connection pooling and move runtime queries to the pooled URL before you load test.
  • Raise the timeout on any task you expect to run longer than the 2-hour default.
  • To set up the plugin for teammates or another machine, see render.com/agents.
  • For deeper rationale, read Render's articles on deploying Next.js applications with SSR and API routes and on triggering a long-running task from a web service.

Frequently asked questions