---
title: Migrate a Python API from Render to Vercel
description: Move a FastAPI, Flask, or Django API from Render to Vercel Functions. Migrate configuration, data, workers, and scheduled jobs.
url: "https://vercel.com/kb/guide/migrate-a-python-api-from-render-to-vercel"
published: 2026-10-14
last_updated: 2026-10-14
authors: Ben Sabic
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

An existing FastAPI, Flask, or Django API on Render can deploy to [Vercel's native Python runtime](https://vercel.com/docs/functions/runtimes/python) through framework detection. Vercel installs the application's Python dependencies and loads its entrypoint, so a Render Start Command isn't needed for the native deployment.

Render also keeps deployment settings outside the application code. Move a Python version pinned in the dashboard into the repository, and map settings in a `render.yaml` Blueprint to Vercel configuration. Persistent disks, background workers, and cron jobs need separate migration decisions before the web service can take production traffic.

## Overview

By the end, you'll have:

- Moved your Python version pin from Render's dashboard into the repository.
  
- Run your Python API on Vercel without a Start Command or `PORT`.
  
- Replaced Render's disks, Postgres, and Key Value with external services.
  
- Moved Celery to Vercel Queues and cron services to Vercel Cron Jobs.
  
- Moved `preDeployCommand` migrations to CI and cut over production traffic.
  

## Prerequisites

- Access to the API's repository and Render service.
  
- A Vercel account and the latest [Vercel CLI](https://vercel.com/docs/cli) installed.
  
- Python and [uv](https://docs.astral.sh/uv/getting-started/installation/) installed locally.
  
- [PostgreSQL client tools](https://www.postgresql.org/download/) for database transfers: `pg_dump`, `pg_restore`, and `psql`.
  

## Should your API move to Vercel?

Before migrating, check that the API and its dependencies fit the native runtime:

- Can sessions, uploads, and other persistent state live in external storage?
  
- Do dependencies install from Python packages and compatible wheels, or does the application need system packages?
  
- Does background work run as bounded queue tasks, or does a process need to stay running indefinitely?
  
- Do requests fit the function's duration and payload limits?
  

Inventory the web service and every worker, private service, and cron job associated with it. If a service needs operating-system packages or an existing Docker image, follow the [Python container deployment path](https://vercel.com/kb/guide/vercel-docker-python-apps), which uses Container Images in Beta. Review shutdown-dependent work separately from request handling.

For an API that uses WebSockets, [Python WebSocket support is in Beta](https://vercel.com/docs/functions/websockets). Connections follow function duration limits, so clients need a reconnection strategy.

With Fluid compute, the default `maxDuration` is 300s. Hobby allows up to 300s; Pro and Enterprise allow up to 800s, with an opt-in extended-duration Beta up to 1,800s. Request and response bodies have a 4.5 MB limit. Check the [function limits](https://vercel.com/docs/functions/limitations) against your routes before cutover.

## How Render concepts map to Vercel

| Render                           | Vercel                                                                                                                  |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Python web service               | Detected Python app on Vercel Functions                                                                                 |
| Docker web service               | Container Images, in Beta                                                                                               |
| Dashboard `PYTHON_VERSION`       | A supported repository version file                                                                                     |
| `startCommand` and `PORT`        | Native Python application entrypoint                                                                                    |
| `buildCommand`                   | Project Build Command or `tool.vercel.scripts.build`                                                                    |
| `envVars` and environment groups | Project variables or linked Shared Environment Variables                                                                |
| Render Postgres                  | External Postgres, including Marketplace providers                                                                      |
| Render Key Value                 | External Redis-compatible storage                                                                                       |
| Persistent disk                  | Object storage or a database                                                                                            |
| Celery background worker         | A subscriber on Vercel Queues, in Beta                                                                                  |
| Cron job service                 | A cron route configured in `vercel.json`                                                                                |
| Private service                  | An internal Vercel Service (Beta) for compatible HTTP applications; other protocols need a separately reachable service |
| `preDeployCommand`               | A migration step in continuous integration                                                                              |
| `healthCheckPath`                | Checks against a deployment URL                                                                                         |
| Preview environment              | Preview deployments and separately configured backing services                                                          |

## Steps

Create a migration branch and keep the Render deployment running until cutover. Skip the database, worker, or cron steps your API doesn't need.

### 1\. Move the Python version pin into the repository

Check the deployed service's Python version, including its dashboard `PYTHON_VERSION` variable. [Render gives that variable precedence](https://render.com/docs/python-version) over `.python-version`, and an unpinned service's default depends on its creation date. Move a dashboard-only pin into a supported repository file for Vercel.

Vercel supports Python 3.12, 3.13, and 3.14, with [3.12 as the default](https://vercel.com/docs/functions/runtimes/python/python-version). Test an upgrade before migrating an application that uses an older version.

For an application tested on Python 3.13, commit a `.python-version` file at the Vercel project root that contains the major and minor version:

```text
3.13
```

Keep the `requires-python` constraint in `pyproject.toml` aligned with your Python version pin. Since an open lower bound such as `>=3.12` also permits 3.14, use a bounded constraint for Python 3.13:

```toml
[project]
name = "my-api"
version = "0.1.0"
requires-python = "~=3.13.0"
dependencies = [
    "fastapi>=0.117.1",
    "sqlalchemy>=2,<3",
    "psycopg[binary]>=3,<4",
    "redis>=5",
    "celery>=5.3,<6",
    "alembic>=1.13,<2",
]
```

Merge the relevant entries into your existing manifest, retaining the application's other dependencies. Keep your existing dependency workflow if it uses a manifest Vercel supports. With uv, generate the lockfile and install its dependencies:

```bash
uv lock
uv sync
```

The commands create or update `uv.lock` and install the resolved packages into `.venv`. Test with that environment, then commit `uv.lock` with the manifest changes. Vercel also accepts `requirements.txt` and Pipenv manifests; [Celery on Vercel](https://vercel.com/docs/frameworks/backend/celery) requires `pyproject.toml`.

For a Poetry project, convert the dependencies to the `[project]` format or export a `requirements.txt` using [Poetry's export plugin](https://github.com/python-poetry/poetry-plugin-export). Don't assume `poetry.lock` alone determines the Vercel build. Check native dependencies against the chosen Python version, especially older `psycopg2-binary` pins without matching wheels.

Python function bundles have a 500 MB uncompressed limit. Exclude tests, local environments, and other unneeded files with [`excludeFiles`](https://vercel.com/docs/functions/runtimes/python); Python builds don't automatically tree-shake the application.

[Large Functions](https://vercel.com/docs/functions/limitations), in Beta, raise the limit to 5 GB on projects using Fluid compute and Active CPU pricing. Check the opt-in requirements for an existing project; Large Functions don't support Secure Compute or Static IPs.

### 2\. Configure the Python entrypoint

This [Render Blueprint](https://render.com/docs/blueprint-spec) starts FastAPI as a web service:

```yaml
services:
  - type: web
    name: my-api
    runtime: python
    buildCommand: pip install -r requirements.txt
    startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT
```

Carry the `main:app` target into the Python entrypoint. Retain required build work, but don't use the server start command as Vercel's Build Command.

Vercel loads the application object from a supported entrypoint, such as `app` in a FastAPI application's `main.py`. Add a health route to your existing application; this minimal example shows the handler:

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
def health():
    return {"status": "ok"}
```

For an undetected entrypoint, set the module path in `pyproject.toml`:

```toml
[tool.vercel]
entrypoint = "my_api.server:app"
```

Use `my_api.server:app` to load `app` from `my_api/server.py`, or `main:app` for the root-level example. [Flask](https://vercel.com/docs/frameworks/backend/flask) also exports `app`; [Django](https://vercel.com/docs/frameworks/full-stack/django) uses its configured WSGI or ASGI application. Set Django's allowed hosts for the deployment and follow its framework instructions for static files.

Keep Gunicorn or Uvicorn if another environment uses them. The native Vercel deployment doesn't use Render's server start command or require a `PORT` setting. Keep source-platform configuration until the source deployment is retired.

### 3\. Replace writes to Render persistent disks

Find every persistent disk mount used by the Render service. Transfer the existing data and update application paths before deploying to Vercel. Render's attached disk cannot be mounted by the new Python deployment.

Move uploaded files and generated artifacts to persistent object storage such as [Vercel Blob](https://vercel.com/docs/vercel-blob). Put sessions and durable application records in Redis or a database, according to their persistence requirements. Replace SQLite files that the running application modifies with an external database.

The function filesystem is read-only except for [up to 500 MB of scratch space in](https://vercel.com/docs/functions/runtimes) [`/tmp`](https://vercel.com/docs/functions/runtimes). Treat that directory as temporary and local to an instance. For uploads above the function body limit, use a [direct client upload](https://vercel.com/docs/vercel-blob/client-upload) flow so the file doesn't pass through the Python API.

### 4\. Provision backing services and environment variables

#### Link the Vercel project

From the repository's application directory, link a new Vercel project:

```bash
vercel link
```

Choose the intended team and project when prompted. Commercial APIs need Pro or Enterprise; [Hobby is limited to non-commercial personal use](https://vercel.com/docs/limits/fair-use-guidelines#commercial-usage).

The CLI saves project metadata in `.vercel`, which should stay out of version control along with exported secrets and database dumps. Add the following entries to `.gitignore` before exporting configuration or pulling local variables:

```text
.vercel/
.venv/
.env*
!.env.example
*.dump
```

#### Review Render variables

Inventory the effective values on the Render service, including its [environment groups and secret files](https://render.com/docs/configure-environment-variables). Service-level variables override group values; resolve duplicate names across groups explicitly.

In `render.yaml`, distinguish literal `envVars` from `fromDatabase` and `fromService` references, then replace service-specific URLs with destination connection strings. Use [Shared Environment Variables](https://vercel.com/docs/environment-variables/shared-environment-variables) for settings shared across projects.

Render secret files, including files under `/etc/secrets`, don't become mounted files on Vercel. Adapt the application to read the secret content from its new configuration. Check the combined environment-variable size before moving file contents into variables.

If a library requires a file path, create a private temporary file under `/tmp` at runtime and pass that path to the library; don't commit the secret to the repository.

#### Add PostgreSQL with Neon

[Neon in the Vercel Marketplace](https://vercel.com/marketplace/neon) provides managed PostgreSQL databases connected to your project. From the linked application directory, use [`vercel integration add`](https://vercel.com/docs/cli/integration#vercel-integration-add) to create a database for each Vercel environment:

```bash
vercel integration add neon --name api-development \
  --environment development --no-env-pull
vercel integration add neon --name api-preview \
  --environment preview --no-env-pull
vercel integration add neon --name api-production \
  --environment production --no-env-pull
```

The CLI installs the integration if needed, prompts for resource settings and a billing plan, and connects each database to the linked project. Scoping each resource with `--environment` keeps development and preview test writes separate from production. The `--no-env-pull` flag skips the automatic variable download; the local testing step pulls Development values when you need them.

Choose a destination PostgreSQL version compatible with the source database and enable any required extensions before restoring data. Place the database near the API's [function region](https://vercel.com/docs/functions/configuring-functions/region) to limit network latency between them.

The integration supplies connection strings for different uses:

| Environment variable    | Use                                                            |
| ----------------------- | -------------------------------------------------------------- |
| `DATABASE_URL`          | Pooled connection for application queries                      |
| `DATABASE_URL_UNPOOLED` | Direct connection for exports, restores, and schema migrations |

If you selected an integration prefix, use the resulting variable names in your application and migration commands. With [Neon preview branching](https://neon.com/docs/guides/vercel-managed-integration), verify the branch assigned to each deployment before running its migrations.

#### Connect Redis if the API uses it

Connect a Redis provider from the [Vercel Marketplace](https://vercel.com/marketplace) and obtain its TCP connection URL. This is typically a `rediss://` URL for TLS; REST credentials such as `KV_REST_API_URL` use a different client interface. Set `REDIS_URL` to the TCP URL and verify the server's TLS certificate as the provider requires.

Render Key Value can hold cache entries, sessions, or queue state. Migrate according to that role; an empty replacement is appropriate only for disposable cache data. In Blueprints, the current service type is `keyvalue`.

#### Add the remaining application secrets

Once the destination services are connected, open the Vercel project's Settings, then Environment Variables, and add the application secrets you retained from Render. Select the appropriate Development, Preview, and Production scopes for each value. Review entries individually so imported Render credentials don't overwrite integration-provided connection strings.

[Vercel's 64 KB limit](https://vercel.com/docs/environment-variables#environment-variable-size) applies to all environment-variable data in a deployment. Large values such as certificates or service-account files can exceed it, so check their size before adding them.

### 5\. Rehearse the database transfer

Confirm export access and install the PostgreSQL client tools. Use matching `pg_dump` and `pg_restore` versions, with `pg_dump` at least as new as the source server. Choose a destination server version no older than the dump tool's major version, and check provider compatibility and required extensions before restoring.

Keep Neon automatic Preview branching disabled for this rehearsal and use the fixed, empty Preview database; Production must remain empty for the final transfer.

Set `SOURCE_DATABASE_URL` and `DESTINATION_DATABASE_URL` in your shell to direct PostgreSQL URLs without SQLAlchemy driver prefixes.

For the destination, copy `DATABASE_URL_UNPOOLED` from the Preview database in the Neon console, and verify its identity before restoring. If the connection string is stored as a project-level Preview variable, you can retrieve it with `vercel env pull .env.preview --environment=preview`.

If you enable automatic Preview branching, the deployment branch may not exist until step 11. Deploy first, identify that branch in Neon, and repeat the restore and migrations against its direct URL before testing it. Branch-specific URLs are injected at deployment time, so an env pull may not include them.

Set `SOURCE_DATABASE_URL` to Render's External Database URL, with access allowed from the machine performing the export. For the [provider's migration procedure](https://neon.com/docs/import/migrate-from-render), export with a direct connection:

```bash
umask 077
pg_dump --format=custom --no-owner --no-acl \
  --file=migration.dump "$SOURCE_DATABASE_URL"
```

The command creates `migration.dump` when it succeeds. Don't use a pooled connection for the export.

If the source is Free Render Postgres, export before its [30-day expiration](https://render.com/docs/free). After a Free database expires, Render gives you 14 days to upgrade it to a paid plan, then deletes the database and its data.

```bash
pg_restore --exit-on-error --no-owner --no-acl \
  --dbname="$DESTINATION_DATABASE_URL" migration.dump
```

Continue only after `pg_restore` exits with status 0. If it fails, fix the cause and retry against a fresh empty destination; `--exit-on-error` doesn't roll back changes.

Recreate any roles and grants the application needs because the restore omits ownership and access-control settings. Compare table counts and representative records, then check constraints and sequence values. For a table named `items`, compare these results:

```bash
psql "$SOURCE_DATABASE_URL" -tAc 'SELECT count(*) FROM items;'
psql "$DESTINATION_DATABASE_URL" -tAc 'SELECT count(*) FROM items;'
```

The counts should match for unchanged test data. Because the live source can receive writes after the dump, counts may differ during a rehearsal.

At the final cutover, pause writers before exporting and compare the restored data while they remain paused. Writes made to Render after the rehearsal still need to be transferred at cutover.

### 6\. Configure the Python database client

For SQLAlchemy with psycopg 3, use this driver and pool configuration:

```python
import os

from sqlalchemy import create_engine
from sqlalchemy.engine import make_url

database_url = make_url(os.environ["DATABASE_URL"]).set(
    drivername="postgresql+psycopg"
)
engine = create_engine(
    database_url,
    pool_size=1,
    max_overflow=0,
    pool_pre_ping=True,
)
```

Use `postgresql+psycopg` for psycopg 3, as shown above, or `postgresql+psycopg2` if the application retains psycopg2.

Each application instance creates its own connection pool, so keep one engine per instance and close checked-out connections after each operation. The example starts with a small pool; tune its size against the provider's connection budget and expected concurrency across all instances.

### 7\. Test the API locally with `vercel dev`

If the API uses a database, apply the migrations from step 10 to Development using its direct connection URL, then add the test records your routes need. The Preview restore from step 5 does not populate this separate database.

Pull the project's Development configuration, then run the API:

```bash
vercel pull --environment=development
uv run vercel dev
```

Use the local URL printed by the CLI. At its default port, the health check is:

```bash
curl --fail http://localhost:3000/health
```

For the entrypoint example, the expected body is:

```json
{"status":"ok"}
```

Exercise a database-backed route such as `/api/items/1` against a known test record. Check cache reads and writes as well as API authentication, since a passing health route alone doesn't test those dependencies.

### 8\. Adapt queue-driven workers to Vercel Queues

Classify each Render `type: worker` service before migrating it. Celery tasks can move to a Vercel subscriber when each invocation fits the function limits. Custom workers that must poll continuously or retain process state need a different design or a service retained on Render.

Vercel Queues is in Beta, and the [Celery integration](https://vercel.com/docs/frameworks/backend/celery) requires Vercel CLI 58.9.0 or later. For an existing Celery application, change its initialization in `tasks.py` and retain the task definitions below it:

```python
from celery import Celery

app = Celery(
    "my-api",
    broker="vercel://",
    backend="vercel-runtime-cache://",
)
app.conf.task_acks_late = True
```

Create a `worker.py` file at the project root that exposes the Celery application as the subscriber entrypoint:

```python
from tasks import app

__all__ = ["app"]
```

Merge these tables into `pyproject.toml`, using your actual web entrypoint. Celery projects declare the web entrypoint explicitly alongside the subscriber, even for a root-level `main.py`. If `[tool.vercel]` already exists from step 2, retain it and add `[[tool.vercel.subscribers]]` below it.

Declare every custom queue through `task_queues` or `task_default_queue` when the Celery application is imported. Queues mentioned only in `task_routes` are not discovered by the Vercel integration. The web and subscriber entrypoints are configured as follows:

```toml
[tool.vercel]
entrypoint = "main:app"

[[tool.vercel.subscribers]]
entrypoint = "worker:app"
```

Restart `vercel dev` to load the new configuration and run the web application and subscriber together. Then call the route that enqueues work, such as `POST /api/reports`, and verify the task's completed result or database side effect.

Tasks can be delivered more than once, so use an idempotent write strategy such as a unique report identifier. Late acknowledgments protect against timeout or crash loss; configure explicit retries for application exceptions.

Store task results that need to persist in Postgres or a supported durable backend because the Runtime Cache result backend is ephemeral.

Replace `celery beat` with a cron route that enqueues work. Celery's `countdown` and `eta` tasks are published immediately and hold a subscriber invocation while they wait, so keep delays short enough for the function duration limit. Use a different scheduling design for longer waits.

For Dramatiq, follow the [Dramatiq configuration](https://vercel.com/docs/frameworks/backend/dramatiq). Other queue libraries need their own adaptation or a worker retained on Render.

### 9\. Replace Render cron services with cron routes

Convert each relevant Render `type: cron` service into an HTTP route. For a service whose `startCommand` is `python -m jobs.cleanup`, reuse the cleanup logic and move its `schedule` to `vercel.json`.

Refactor `jobs/cleanup.py` so it exports a `run()` function containing the existing job logic. Retain a `main` guard if Render still runs the module. This example assumes `run()` returns the number of deleted records. Add the following imports and route to your existing FastAPI `main.py`:

```python
import hmac
import os

from fastapi import Header, HTTPException
from jobs.cleanup import run as cleanup

@app.get("/api/cron/cleanup")
def cleanup_job(authorization: str | None = Header(default=None)):
    secret = os.environ.get("CRON_SECRET")
    if not secret:
        raise HTTPException(status_code=503, detail="Cron secret is not configured")
    expected = f"Bearer {secret}"
    if not authorization or not hmac.compare_digest(
        authorization.encode(), expected.encode()
    ):
        raise HTTPException(status_code=401, detail="Unauthorized")
    if os.environ.get("JOBS_ENABLED") != "true":
        return {"status": "disabled"}
    return {"status": "ok", "deleted": cleanup()}
```

Set a random `CRON_SECRET` in Vercel's environment variables and leave `JOBS_ENABLED=false` in Production until the scheduler handoff. This example introduces `JOBS_ENABLED` to prevent the route from doing work before cutover.

Merge the schedule into `vercel.json`:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "crons": [
    {"path": "/api/cron/cleanup", "schedule": "0 3 * * *"}
  ]
}
```

[Vercel Cron Jobs](https://vercel.com/docs/cron-jobs/manage-cron-jobs) send an HTTP GET to the production route. When `CRON_SECRET` is set, the request includes it as a bearer token in the `Authorization` header, which the route above checks.

Schedules use UTC and numeric values; translate named days or months. The example runs daily at 03:00 UTC. Hobby rejects schedules more frequent than daily and invokes within the specified hour; Pro and Enterprise support minute-level schedules. Each project can have up to 100 cron jobs.

Make the cleanup operation safe to repeat, and use a lock if overlapping runs would cause problems. Cron delivery is best effort, and failed invocations aren't retried automatically. Monitor failures and provide a way to rerun missed work.

Test missing and incorrect credentials for a 401 response. In an isolated test environment with `JOBS_ENABLED=true`, a valid request should return `status: ok` and the deletion count.

### 10\. Move `preDeployCommand` migrations to CI

Render's `preDeployCommand` runs before the web service is updated, so preserve that ordering by moving schema migrations into the release pipeline. Vercel's Python entrypoint doesn't execute the command from `render.yaml`.

Run migrations once from your continuous integration pipeline before promotion. Check out the same commit, install the pinned Python version and uv, and run from the application root. Use the Preview database for rehearsal; migrate Production only after its final restore in step 12.

Bind a CI secret named `DATABASE_URL_UNPOOLED` to the intended destination's direct URL. For a local rehearsal, set that shell variable explicitly; `vercel pull` does not export it into your terminal session. If Alembic reads `DATABASE_URL`, use:

```bash
: "${DATABASE_URL_UNPOOLED:?Set the intended direct database URL}" &&
  uv sync --locked &&
  DATABASE_URL="$DATABASE_URL_UNPOOLED" uv run --locked alembic upgrade head
```

Make deployment promotion depend on the migration job succeeding. If Alembic reads a URL from another setting, update `alembic/env.py` to read the intended variable; setting a shell variable alone won't change that configuration. Apply the explicit SQLAlchemy driver selection in the migration configuration too. For Django, run `python manage.py migrate` with database settings for the same destination.

Keep schema changes compatible with the old application while it is still running. Avoid migrations in request handlers, application startup, or a preview build that can reach production data.

### 11\. Verify a preview deployment

For a new project, [Vercel makes the first deployment Production](https://vercel.com/docs/cli/deploy#prod), even without `--prod`. Before deploying it, temporarily bind Production variables to isolated test databases, Redis, and storage. Keep `JOBS_ENABLED=false` and disable startup work and other task producers. Deploy without assigning production domains:

```bash
vercel deploy --prod --skip-domain
```

Confirm that deployment succeeds. Leave Production connected to the isolated test services until step 12; the final Production database must remain empty. Skip this initialization if Production already has a successful deployment.

Then deploy the migration branch to Preview:

```bash
PREVIEW_URL="$(vercel deploy)"
```

`vercel deploy` prints the deployment URL to standard output, so this command stores it in `PREVIEW_URL`. Inspect that deployment and test the health route:

```bash
vercel inspect "$PREVIEW_URL" --logs
vercel curl /health --deployment "$PREVIEW_URL"
vercel logs --deployment "$PREVIEW_URL" --query 'level:error'
```

The build log should show your pinned Python minor version and successful dependency installation. The health route should return 200. `vercel curl` supports testing a deployment protected by Vercel's Deployment Protection.

Verify authenticated routes and representative database writes on preview data. Enqueue a task and wait for its result. Call the cron route manually with test credentials; scheduled invocation only happens in Production. Confirm that long requests and representative uploads fit the configured limits.

If a route needs longer than the 300s default, merge a function entry keyed by the resolved entrypoint file into `vercel.json`. This example uses the 800s maximum available on Pro and Enterprise:

```json
{
  "functions": {
    "main.py": {"maxDuration": 800}
  }
}
```

Use your actual filename and retain the `crons` entry. Test any FastAPI lifespan cleanup that the API relies on; [shutdown has a 500 ms window](https://vercel.com/docs/frameworks/backend/fastapi), so durable work shouldn't depend on a long shutdown handler.

### 12\. Cut over production traffic

Set Production variables to the final backing services and keep `JOBS_ENABLED=false`. Replace any bindings to isolated test services from the initial deployment.

Keep API write endpoints in maintenance mode until the final transfer, promotion, and domain checks are complete; `JOBS_ENABLED` only gates the example cron route. The staged deployment uses Production configuration even though production domains are not assigned to it, so disable startup side effects and background producers before creating it:

```bash
PRODUCTION_DEPLOYMENT_URL="$(vercel deploy --prod --skip-domain)"
```

The command stores the staged deployment's URL in `PRODUCTION_DEPLOYMENT_URL` for promotion after the final data transfer.

Schedule a write pause for the final dump and restore:

1. Stop new writes through the Render API, including uploads, and disable schedules and task producers. Stop administrative scripts and one-off jobs that can write to Render's data stores; suspending a Render service does not terminate its active one-off jobs.
   
2. Wait for in-flight API requests and running tasks to finish, and account for pending, delayed, or retrying messages. Stop the old queue consumers and confirm that all writers are paused before exporting. Replay retained work only against the chosen destination.
   
3. Verify the source and destination identities, then set `DESTINATION_DATABASE_URL` to Production's `DATABASE_URL_UNPOOLED`. Confirm the destination is empty. If it isn't, stop and prepare a fresh empty destination; update Production variables and rebuild the staged deployment if its connection changes.
   
4. Take the final export and restore it to the empty destination. Continue only after the export and restore succeed, then run the required migrations.
   
5. Synchronize files added, changed, or deleted since the rehearsal. Skip this transfer if both deployments already use the same destination storage.
   
6. Transfer retained Redis application data using the provider's supported migration method, preserving expirations where required. If losing existing sessions is acceptable, explicitly invalidate them instead.
   
   Rebuild disposable caches, and handle broker messages through the drain or replay decision above. If both deployments share the same retained Redis service, verify that the new application can reach the authoritative data.
   

With writers paused, verify the final database, files, and retained Redis data. Keep API writes disabled until promotion and domain checks are complete.

Promote the staged deployment after testing its routes against the final data:

```bash
vercel promote "$PRODUCTION_DEPLOYMENT_URL"
```

[Add the custom domain](https://vercel.com/docs/domains/working-with-domains/add-a-domain) to Vercel and apply its DNS records. Confirm HTTPS and API behavior through the domain before enabling API writes. Replace Render's hostname in allowed origins, callback URLs, and webhook destinations.

Keep the old application from writing to its old database while cached DNS records still route traffic there.

When the Render scheduler is disabled and its work has finished, set `JOBS_ENABLED=true` for Production and redeploy. Confirm one scheduled execution and check for duplicate work. Environment-variable changes require a new deployment to take effect.

### 13\. Retire unused Render resources after the rollback window

Keep Render resources available for the rollback window you chose before cutover. Monitor errors, database connections, and task completion under production traffic before removing unused services.

> **Warning:**
> 
> If Vercel has accepted new writes, sending traffic back to the old application and database can lose those changes. Reconcile data or point the compatible old application at the authoritative database before reverting traffic. Vercel's application rollback doesn't reverse database migrations or restore data.

After the rollback window, retain the required backups, remove unused Render services, and clean up source-specific configuration. Delete local secret exports and database dumps when they are no longer needed.

## What doesn't map from Render

Persistent disks need a storage migration before the API moves. Background workers that require a continuously running process need to stay on Render or be redesigned around bounded tasks.

Render's service health checks and pre-deploy settings don't carry over from a Blueprint. Test the deployed API explicitly and make migrations a pipeline gate. If the application uses private services, review the [Services configuration](https://vercel.com/docs/services) separately, including access and environment bindings.

## Troubleshooting

### The build ignores Render's `PYTHON_VERSION` setting

Specify a supported version in `.python-version` or `requires-python`. Check the configured project root and resolve conflicts between those settings.

### A package fails to install or import

Check that the selected Python version matches the application's tested dependencies. Without a compatible wheel, installation may require a native build. Confirm the dependency is declared in the manifest Vercel uses, especially after converting from Poetry.

### Every route returns 404

Confirm the Vercel project's root directory contains the dependency manifest and entrypoint. Check the exported application name and custom `tool.vercel.entrypoint`, then inspect the deployment's detected framework. Avoid carrying over container rewrites that point away from the native app.

### Requests exceed the payload or duration limit

For a 413 `FUNCTION_PAYLOAD_TOO_LARGE`, send the file directly to object storage. For a 504 `FUNCTION_INVOCATION_TIMEOUT`, inspect slow database or external-service calls before increasing `maxDuration` within the plan's limit. Split work that still exceeds the limit into smaller tasks.

### The database connection uses a Render-only address

Check the active deployment's `DATABASE_URL`. An address intended for Render's private network won't connect from Vercel. Use the destination provider's URL; use Render's External Database URL only to export data or connect to a database you plan to retain.

### A cron job doesn't run in preview

Vercel schedules run against Production. Test the handler manually on the preview deployment, then check the active production schedule, `CRON_SECRET`, and application gate during cutover.

## Next steps

- [Python runtime reference](https://vercel.com/docs/functions/runtimes/python)
  
- [Deploy a FastAPI app on Vercel](https://vercel.com/docs/frameworks/backend/fastapi)
  
- [Run Celery on Vercel](https://vercel.com/docs/frameworks/backend/celery)
  
- [Vercel Cron Jobs](https://vercel.com/docs/cron-jobs)
  
- [How to migrate from Render to Vercel](https://vercel.com/kb/guide/migrate-render-to-vercel)
  
- [Running Docker on Vercel vs Render](https://vercel.com/kb/guide/docker-on-vercel-vs-render)
  

## Frequently asked questions

### Can I keep using render.yaml on Vercel?

No. Translate the relevant Blueprint settings into application files, Vercel project settings, and `vercel.json`. Keep `render.yaml` while Render still runs the source application or any retained services.

### Does Vercel read Render's PYTHON_VERSION variable?

No. Vercel reads the Python version from `pyproject.toml`, `.python-version`, or `Pipfile.lock`. Move the version resolved by Render into one of those files and confirm the result in the deployment build log.

### Can my Render background worker move to Vercel?

Celery and Dramatiq integrate with Vercel Queues, in Beta, for bounded queue tasks. Custom workers that must poll continuously or retain process state need to stay on Render or be redesigned.

### How do I export Render Postgres?

Use the External Database URL with `pg_dump` and restore the archive into the destination provider. Check external access rules and avoid a pooled connection for the export. Export Free Render Postgres before its 30-day expiration.

### What replaces Render's health check path?

Validate the Vercel deployment through its URL and use those checks to gate the release. Keep a health route for diagnostics, but don't assume the `healthCheckPath` field in `render.yaml` controls the new deployment.

## More Vercel Functions guides

- [Migrate a Python API from Railway to Vercel](/kb/guide/migrate-a-python-api-from-railway-to-vercel): Move a FastAPI, Flask, or Django API from Railway to Vercel Functions. Migrate configuration, data, workers, and scheduled jobs.
- [Migrate a Python API from Heroku to Vercel](/kb/guide/migrate-a-python-api-from-heroku-to-vercel): Move a FastAPI, Flask, or Django API from Heroku to Vercel Functions. Migrate configuration, data, workers, and scheduled jobs.
- [Build Figma-style multiplayer cursors with WebSockets on Vercel](/kb/guide/real-time-board-nextjs-fastapi): Learn how to build Figma-style multiplayer cursors with Next.js and FastAPI, kept consistent across multiple Vercel Function instances using WebSockets and Redis.