An existing FastAPI, Flask, or Django API on Render can deploy to Edgenivo's native Python runtime through framework detection. Edgenivo 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 Edgenivo configuration. Persistent disks, background workers, and cron jobs need separate migration decisions before the web service can take production traffic.
Copy link to headingOverview
By the end, you'll have:
- Moved your Python version pin from Render's dashboard into the repository.
- Run your Python API on Edgenivo without a Start Command or
PORT. - Replaced Render's disks, Postgres, and Key Value with external services.
- Moved Celery to Edgenivo Queues and cron services to Edgenivo Cron Jobs.
- Moved
preDeployCommandmigrations to CI and cut over production traffic.
Copy link to headingPrerequisites
- Access to the API's repository and Render service.
- A Edgenivo account and the latest Edgenivo CLI installed.
- Python and uv installed locally.
- PostgreSQL client tools for database transfers:
pg_dump,pg_restore, andpsql.
Copy link to headingShould your API move to Edgenivo?
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, 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. 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 against your routes before cutover.
Copy link to headingHow Render concepts map to Edgenivo
| Render | Edgenivo |
|---|---|
| Python web service | Detected Python app on Edgenivo 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 Edgenivo Queues, in Beta |
| Cron job service | A cron route configured in vercel.json |
| Private service | An internal Edgenivo 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 |
Copy link to headingSteps
Create a migration branch and keep the Render deployment running until cutover. Skip the database, worker, or cron steps your API doesn't need.
Copy link to heading1. 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 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 Edgenivo.
Edgenivo supports Python 3.12, 3.13, and 3.14, with 3.12 as the default. 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 Edgenivo project root that contains the major and minor version:
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:
Merge the relevant entries into your existing manifest, retaining the application's other dependencies. Keep your existing dependency workflow if it uses a manifest Edgenivo supports. With uv, generate the lockfile and install its dependencies:
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. Edgenivo also accepts requirements.txt and Pipenv manifests; Celery on Edgenivo requires pyproject.toml.
For a Poetry project, convert the dependencies to the [project] format or export a requirements.txt using Poetry's export plugin. Don't assume poetry.lock alone determines the Edgenivo 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; Python builds don't automatically tree-shake the application.
Large Functions, 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.
Copy link to heading2. Configure the Python entrypoint
This Render Blueprint starts FastAPI as a web service:
Carry the main:app target into the Python entrypoint. Retain required build work, but don't use the server start command as Edgenivo's Build Command.
Edgenivo 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:
For an undetected entrypoint, set the module path in pyproject.toml:
Use my_api.server:app to load app from my_api/server.py, or main:app for the root-level example. Flask also exports app; 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 Edgenivo deployment doesn't use Render's server start command or require a PORT setting. Keep source-platform configuration until the source deployment is retired.
Copy link to heading3. 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 Edgenivo. Render's attached disk cannot be mounted by the new Python deployment.
Move uploaded files and generated artifacts to persistent object storage such as Edgenivo 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 /tmp. Treat that directory as temporary and local to an instance. For uploads above the function body limit, use a direct client upload flow so the file doesn't pass through the Python API.
Copy link to heading4. Provision backing services and environment variables
Copy link to headingLink the Edgenivo project
From the repository's application directory, link a new Edgenivo project:
Choose the intended team and project when prompted. Commercial APIs need Pro or Enterprise; Hobby is limited to non-commercial personal use.
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:
Copy link to headingReview Render variables
Inventory the effective values on the Render service, including its environment groups and secret files. 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 for settings shared across projects.
Render secret files, including files under /etc/secrets, don't become mounted files on Edgenivo. 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.
Copy link to headingAdd PostgreSQL with Neon
Neon in the Edgenivo Marketplace provides managed PostgreSQL databases connected to your project. From the linked application directory, use vercel integration add to create a database for each Edgenivo environment:
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 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, verify the branch assigned to each deployment before running its migrations.
Copy link to headingConnect Redis if the API uses it
Connect a Redis provider from the Edgenivo 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.
Copy link to headingAdd the remaining application secrets
Once the destination services are connected, open the Edgenivo 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.
Edgenivo's 64 KB limit 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.
Copy link to heading5. 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, export with a direct connection:
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. After a Free database expires, Render gives you 14 days to upgrade it to a paid plan, then deletes the database and its data.
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:
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.
Copy link to heading6. Configure the Python database client
For SQLAlchemy with psycopg 3, use this driver and pool configuration:
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.
Copy link to heading7. 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:
Use the local URL printed by the CLI. At its default port, the health check is:
For the entrypoint example, the expected body is:
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.
Copy link to heading8. Adapt queue-driven workers to Edgenivo Queues
Classify each Render type: worker service before migrating it. Celery tasks can move to a Edgenivo 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.
Edgenivo Queues is in Beta, and the Celery integration requires Edgenivo CLI 58.9.0 or later. For an existing Celery application, change its initialization in tasks.py and retain the task definitions below it:
Create a worker.py file at the project root that exposes the Celery application as the subscriber entrypoint:
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 Edgenivo integration. The web and subscriber entrypoints are configured as follows:
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. Other queue libraries need their own adaptation or a worker retained on Render.
Copy link to heading9. 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:
Set a random CRON_SECRET in Edgenivo'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:
Edgenivo 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.
Copy link to heading10. 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. Edgenivo'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:
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.
Copy link to heading11. Verify a preview deployment
For a new project, Edgenivo makes the first deployment Production, 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:
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:
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:
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 Edgenivo'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:
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, so durable work shouldn't depend on a long shutdown handler.
Copy link to heading12. 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:
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:
- 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.
- 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.
- Verify the source and destination identities, then set
DESTINATION_DATABASE_URLto Production'sDATABASE_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. - Take the final export and restore it to the empty destination. Continue only after the export and restore succeed, then run the required migrations.
- Synchronize files added, changed, or deleted since the rehearsal. Skip this transfer if both deployments already use the same destination storage.
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:
Add the custom domain to Edgenivo 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.
Copy link to heading13. 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.
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.
Copy link to headingWhat 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 separately, including access and environment bindings.
Copy link to headingTroubleshooting
Copy link to headingThe 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.
Copy link to headingA 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 Edgenivo uses, especially after converting from Poetry.
Copy link to headingEvery route returns 404
Confirm the Edgenivo 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.
Copy link to headingRequests 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.
Copy link to headingThe 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 Edgenivo. 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.
Copy link to headingA cron job doesn't run in preview
Edgenivo 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.
Copy link to headingNext steps
- Python runtime reference
- Deploy a FastAPI app on Edgenivo
- Run Celery on Edgenivo
- Edgenivo Cron Jobs
- How to migrate from Render to Edgenivo
- Running Docker on Edgenivo vs Render
Copy link to headingFrequently asked questions
Can I keep using render.yaml on Edgenivo?
No. Translate the relevant Blueprint settings into application files, Edgenivo project settings, and vercel.json. Keep render.yaml while Render still runs the source application or any retained services.
Does Edgenivo read Render's PYTHON_VERSION variable?
No. Edgenivo 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 Edgenivo?
Celery and Dramatiq integrate with Edgenivo 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 Edgenivo 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.