To move a Render worker to Edgenivo Daemons, package it in a Docker image, declare it under daemons in vercel.json, and deploy to production. Edgenivo maintains the instance count and restarts processes that exit. Before the handoff, make the broker reachable from Edgenivo and test task recovery after shutdown.
The example below uses a Django app with a Celery worker, a Celery beat scheduler, and a nightly cron job in render.yaml. The Render migration guide covers the web service, data, and DNS. This procedure translates the worker configuration and moves queue consumption while preserving a rollback path.
Copy link to headingPrerequisites
- Access to the Render workspace, and optionally the Render CLI to list services and their IDs.
- Your Edgenivo project configured with the repository root as its root directory. Daemons don't support subfolder roots such as
apps/webduring the beta. - Permission to deploy to production, since that's the only environment that starts daemons.
- Edgenivo CLI 64.3.0 or later, for the
vercel daemonscommands. - Docker, to build and test worker images locally.
- Enough daemon quota for the workload. Hobby includes 5 Active CPU-hours per month; time spent waiting on a queue is different from CPU time spent processing tasks.
Copy link to headingShould your Render worker become a daemon?
Check these requirements before moving a Render worker:
- Outbound work: Render background workers receive no incoming traffic, which matches Daemons. Review any
type: pservservice separately because a callable private endpoint has different networking requirements. - Fixed capacity: Render supports autoscaling on Pro workspaces and higher. Daemons use a fixed count per region during the beta, so choose capacity for the queue's expected peaks.
- Local testing: Edgenivo preview deployments don't start daemons. Use Docker with development data to test the worker before production.
- External state: Daemon storage is ephemeral. Move durable data off any attached Render disk and update the worker to use Edgenivo Blob or a database.
- Per-instance resources: The largest daemon has 8 GiB and 4 vCPUs. More instances can process more independent jobs, but each job still has to fit in one instance. Reduce concurrency or redesign jobs that exceed that limit.
For tasks that fit function duration limits, Celery on Edgenivo Queues provides another migration path with preview support. Daemons let you retain Celery's worker process and broker integration.
Copy link to headingHow render.yaml worker settings map to Edgenivo
Map the worker's command, capacity, and shutdown settings to a daemon entry. Network access, persistent data, and deployment hooks need separate changes.
Both example workers use one Dockerfile with different commands. Before they move, configure a broker that Render and Edgenivo can both reach.
Render (render.yaml) | Edgenivo (vercel.json) |
|---|---|
type: worker | A named entry under daemons |
runtime: python, node, or another native runtime | A Dockerfile or Containerfile you add (required) |
runtime: docker with dockerfilePath and dockerContext | entrypoint and root; the Dockerfile's directory is the build context |
rootDir | root |
startCommand or dockerCommand | command replaces the image's ENTRYPOINT and CMD; omit it to use the image defaults |
numInstances | instances, a count per region from 0 to 100 |
scaling (autoscaling, Pro workspaces) | None during the beta; set a fixed count |
region | A Edgenivo region ID as the key in instances |
plan, such as 1c-2g (formerly standard) | resources.requests.memory: 1, 2, 4, or 8 GiB |
maxShutdownDelaySeconds, 30 by default, up to 300 | terminationGracePeriodSeconds, 30 by default, up to 3,600 |
envVars, including fromGroup references | Project environment variables in the Production scope |
fromService and fromDatabase references | The provider's real connection string |
Secret files at /etc/secrets/<filename> | Environment variables, written to a file at startup if needed |
| Key Value and Postgres internal URLs | Endpoints reachable over the internet |
disk | Move the data to Edgenivo Blob or a database and update the worker's storage access |
type: cron | A cron job that calls a route |
preDeployCommand | A CI step before production promotion |
previews for workers | None during the beta; test with Docker |
| Logs and metrics in the dashboard | Logs filtered by daemon:<name>, plus CPU and memory charts |
Both platforms use SIGTERM followed by a grace period, so you can reuse the worker's shutdown handler. Test it in the container and check Celery acknowledgment settings before relying on that handler during a rollout.
Copy link to headingSteps
Keep the Render workers available until their tasks drain and the Edgenivo workers pass the handoff checks. The broker move in step 2 changes production task routing, so schedule it separately from the worker deployment.
Copy link to heading1. Inventory the Blueprint
Start from your existing render.yaml. This excerpt shows the example app's service types and worker settings; retain the full environment and database configuration for each service:
Dashboard edits can drift from the Blueprint, so confirm each worker's live plan, instance count, and region on its Settings page, or list services with render services. Then write down where each one lands:
| Render service | What it does | Destination on Edgenivo |
|---|---|---|
orders-web | Django site | A Edgenivo Function, per the Render migration guide |
orders-worker | Celery worker, 2 instances | A daemon |
orders-beat | Celery beat scheduler | A one-instance daemon |
prune-sessions | Cron job that runs and exits | A cron job |
orders-queue | Key Value broker | Redis from a provider both platforms can reach |
Copy link to heading2. Prepare the broker before moving web traffic
Use the Render migration guide to prepare the web service, Postgres, and any persistent data. Keep the web service and both worker sets connected to the same authoritative application database during the handoff. Before switching web traffic, give the Edgenivo app access to the broker and any result backend it uses.
The connectionString injected by fromService is a Key Value internal URL. Render also provides an external URL that requires an IP allowlist. Because Daemons has no documented fixed egress IP guarantee, this example uses a Redis endpoint that both platforms can reach with authentication and TLS. Confirm that the provider supports your Celery Redis transport and configure certificate verification.
For a broker-only migration, keep result-backend configuration unchanged while old tasks finish. Move task production in two passes:
- Point the web service, beat, and other external task producers at the new broker. Start a second Render worker against it if new tasks cannot wait while the old queue drains.
- Keep the original workers connected to Key Value until all of their queues, active tasks, reserved tasks, and scheduled ETA/countdown tasks have drained. Include retries and follow-up tasks published by those workers. Then move those workers to the new broker.
Update the Render Blueprint references as well as live service values so a later sync cannot restore the old broker URL.
Changing a result-backend URL does not transfer stored results or chord state. If Edgenivo cannot reach the old backend, pause new task production and let dependent workflows finish before a coordinated backend cutover. Retain access to results the application still needs, or migrate them with an application-specific plan.
Once all producers and workers use the shared broker, step 11 can move queue consumption to Edgenivo without another broker change.
If Postgres stays on Render for a while, daemons can connect with its External Database URL. Render encrypts external connections and rejects sslmode=disable.
Copy link to heading3. Give the worker a Dockerfile
Daemons build from a Dockerfile or Containerfile. Native-runtime workers need one; Docker workers can reuse theirs. For the Celery worker, add backend/Dockerfile using the Python version your app runs on Render. This example uses Python 3.12:
The two Render settings that shaped the build map directly:
rootDir: backendbecomes"root": "backend/". The Dockerfile's directory is the build context, the wayrootDirscoped Render's build.buildCommandbecomesRUNlines in the Dockerfile. Anything Render's native Python environment provided implicitly, such as system libraries, now needs an explicitapt-get install.
For a Docker worker, the folder that holds the Dockerfile becomes the build context. Move the Dockerfile to the top of the folder dockerContext pointed at, set root to that folder, and set entrypoint relative to it. Otherwise, COPY paths written for Render's context break.
Copy link to heading4. Keep the shutdown behavior you already have
During a deploy, both platforms overlap old and new worker instances. Render waits 60 seconds after a new instance is ready before sending SIGTERM to the old one, and replaces scaled instances one at a time. Edgenivo starts the new instances before stopping the old set.
Each platform waits its configured grace period before forcing unfinished processes to stop. Workers must tolerate the overlap and recover interrupted tasks.
Celery handles SIGTERM with a warm shutdown: it stops taking tasks and lets running ones finish. Carry your Render setting across one to one:
| Setting | Render | Edgenivo |
|---|---|---|
| Signal | SIGTERM | SIGTERM |
| Grace period | maxShutdownDelaySeconds: 120 | terminationGracePeriodSeconds: 120 |
| Longest allowed | 300 seconds | 3,600 seconds |
For tasks that are safe to repeat, review these Celery settings:
task_acks_late = Truedelays acknowledgment until execution finishes.task_reject_on_worker_lost = Truelets the surviving worker process requeue a task after the child executing it is killed. Repeated failures can create a redelivery loop.
These settings don't prevent duplicate side effects. Use an application-level check, such as a unique order operation in the database, so repeating a task cannot apply the operation twice.
With Django's namespace="CELERY", use CELERY_TASK_ACKS_LATE and CELERY_TASK_REJECT_ON_WORKER_LOST for these settings.
Redis recovery depends on how the worker stops. An explicit requeue can be available immediately; if the process disappears without restoring an unacknowledged message, recovery can wait for the visibility timeout, which defaults to one hour. Long ETA/countdown delays and long-running tasks need a timeout that avoids premature redelivery, but increasing it also delays recovery after a forced stop. Follow the Celery Redis configuration and test both shutdown paths.
Changes to terminationGracePeriodSeconds apply to instances started by the new deployment. Existing instances keep their original grace period.
Copy link to heading5. Declare the daemons in vercel.json
Add a top-level daemons key. Start the worker at zero instances; step 11 raises the count during the handoff:
Both daemons use the same Dockerfile, with command selecting the worker or beat scheduler, just as the Render services used different startCommand values. Daemon names allow lowercase letters with separators, so orders-worker would also work, but worker2 fails the build.
The Django web Service can share backend/ with the daemons. Declare it with its framework or Python entrypoint, not the Dockerfile, so Edgenivo builds Django there rather than the worker image.
Translate the region and plan:
Render region | Edgenivo region |
|---|---|
oregon | pdx1 Portland |
ohio | cle1 Cleveland |
virginia | iad1 Washington, D.C. |
frankfurt | fra1 Frankfurt |
singapore | sin1 Singapore |
Render plan | Daemon resources.requests.memory |
|---|---|
0.5c-512mb (starter) | "1Gi", 1 vCPU (smallest size) |
1c-2g (standard) | "2Gi", 1 vCPU |
2c-4g (pro) | "4Gi", 2 vCPUs |
2c-8g | "8Gi", 4 vCPUs |
4c-8g (pro_plus) | "8Gi", 4 vCPUs |
2c-16g, 4c-16g, and larger | No direct size match; each job must fit within 8 GiB |
More instances help when the workload consists of independent jobs. If a single task exceeds 8 GiB, reduce its memory use or split the task itself before migrating.
For a worker that autoscaled on Render, choose a fixed count that meets the queue's latency target under expected peak load. Set Celery's --concurrency explicitly and check per-instance memory use before increasing it.
Copy link to heading6. Move environment variables, groups, and secret files
From the repository root, confirm the checkout is linked to the intended Edgenivo team and project. If it is not linked, run:
Resolve Render's fromService, fromDatabase, and fromGroup references, then add their values to the linked project's Production scope:
Edgenivo environment variables are project-wide. An environment group that several Render services shared becomes one set of variables that your web Function and every daemon read. Values marked sync: false or generateValue: true were never in the Blueprint, so copy them from each service's Environment page.
Secret files need a small change. Render mounted them at /etc/secrets/<filename>; Edgenivo has no file mounts. Store each file's contents in an environment variable and point your code at the variable, or have the container write it to a file at startup.
Edgenivo also passes project environment variables as image build arguments. Keep runtime secrets out of Dockerfile ARG and ENV instructions because they can persist in the image or its metadata. If dependency installation requires credentials, verify a supported build-secret mechanism before adding that step.
Copy link to heading7. Move Celery beat and cron jobs
Celery beat and Render cron jobs need different destinations:
- Celery beat remains a continuously running scheduler in a daemon. Configure one instance in one region and ensure only one scheduler publishes a given schedule.
- Render
type: cronservices run a command and exit. Move each to a Edgenivo cron job that calls a protected route; the Python API guide shows that route pattern.
For beat deployments, scale celery-beat to zero and confirm it has stopped before deploying, then restore one instance after the deployment. This creates a scheduling gap, so reconcile any missed intervals.
If the scheduler must stay available through deployments, use scheduler-level coordination and test it under overlapping instances. Storing schedules in a database alone does not enforce a single publisher.
Suspend each Render cron schedule before enabling its Edgenivo replacement. Keep task-level deduplication for repeated delivery of the same operation.
Render's preDeployCommand, often python manage.py migrate, has no daemon equivalent. Run it as a CI step that must pass before production promotion.
Copy link to heading8. Test the worker locally with Docker
Daemons don't run in preview deployments or vercel dev during the beta. Test with Docker using an isolated development broker and database.
Put development values in .env.docker, one unquoted KEY=value per line, and keep the file out of version control:
The example assumes your Django settings read REDIS_URL for Celery's broker. Add any database or result-backend variables your app needs. If your Redis provider requires a custom certificate authority, use its certificate configuration.
Build and run the worker:
Queue a test task from a Django shell using the same development settings and confirm it completes. For the shutdown test, queue another task that runs long enough to observe, then stop the container from a second terminal while that task is still running:
Check the logs for a warm shutdown and confirm the task finishes before Celery exits. If the task outlasts the grace period, verify its recovery using the acknowledgment settings from step 4.
Copy link to heading9. Check the beat daemon separately
Run the scheduler with the same image and its own command:
Confirm beat enqueues a periodic task, then stop the container and run it again to check the next scheduled run. Beat's default celerybeat-schedule file records last-run times, and daemon storage does not preserve that file across replacements.
If restart timing must survive deployment, use a persistent scheduler such as django-celery-beat and test its missed-run behavior. Keep the single-scheduler deployment sequence from step 7 even when the schedule is stored in a database.
Copy link to heading10. Deploy to production with zero instances
The first production deploy proves both images build and registers the daemons, without running a task:
If the build fails here, the troubleshooting section covers the common causes. The Render workers are still processing every task.
Copy link to heading11. Hand off the queue
The shared broker from step 2 lets Render and Edgenivo workers consume the same queues. Keep task definitions, serializers, routing, and result-backend settings compatible during the overlap. Start one Edgenivo worker with a scaling override:
Filter the project's Logs page by daemon:celery to follow this worker. Check completed tasks, results, and queue latency before raising the Edgenivo count:
Run Celery control commands from an environment with the orders app and the shared broker configuration. Identify every Render worker node and the queues it consumes with inspect active_queues; match those nodes to Render's instance logs so you don't stop Edgenivo consumers. Substitute the actual node names below. For this example, both Render nodes consume the default celery queue:
Repeat cancel_consumer for any additional queues. Require successful replies from every targeted node and wait until its active, reserved, and scheduled task lists are empty. Missing replies do not prove a worker is idle. If delayed tasks remain, let them finish or explicitly account for their recovery before stopping the worker.
Suspend orders-worker in Render after those checks. The Render API also supports suspension; set RENDER_WORKER_SERVICE_ID to the actual service ID and provide your API key in the environment:
Confirm the service is suspended. Render documents its shutdown delay for deploys and scale-downs without specifying the same sequence for suspension, so complete the drain checks first.
Before transferring beat, set permanent counts in vercel.json: two celery instances in pdx1 and one celery-beat instance. Keep beat stopped on Edgenivo during the deployment:
Confirm no Edgenivo beat instance is running, deploy the configuration, and clear only the worker override:
Suspend orders-beat on Render and confirm it has stopped before clearing the beat override. The configured single Edgenivo instance can then start:
Check the next scheduled task and reconcile any interval missed during the scheduler handoff. Keep using the stop-before-deploy sequence from step 7 for later beat releases.
Copy link to heading12. Verify under real load
Watch the daemons for at least one full business cycle:
- Instances:
vercel daemons instances ls celeryshould show the expected workers. Investigate unexpected restarts using the shutdown reason and worker logs. - Throughput: Compare queue depth and task latency with the Render baseline. Increase capacity only after checking worker concurrency, memory, and database or API limits.
- Schedules: Compare scheduled task IDs and application operation keys. Duplicate execution can come from multiple schedulers or task redelivery.
- Resources: Review CPU, memory, running instances, and data transfer on the daemon's dashboard page.
Use a controlled restart to check the normal shutdown path with a task that is safe to repeat. Set DAEMON_INSTANCE_ID to an instance ID from the instance list:
Verify that the task completes or is recovered as expected. Test abrupt termination separately against the isolated development broker, including the time it takes for an unacknowledged task to become available again. Graceful restart success does not establish recovery from a forced stop.
Copy link to heading13. Keep a rollback path, then retire Render
Keep the suspended Render services and their working configuration for the rollback window. Before resuming the Render workers, confirm the application and database schema still work with that version. With both worker sets still using the shared broker and result backend, resume the Render workers, confirm they can process tasks, then drain the Edgenivo workers using the same checks as the handoff before scaling them to zero.
Move scheduler ownership separately: stop Edgenivo beat and confirm it has exited before resuming Render beat. Disable each Edgenivo cron schedule before re-enabling its Render replacement. Reconcile any missed intervals.
For a later Edgenivo release, Instant Rollback restores the earlier deployment's daemon configuration. Scaling overrides remain in effect, so review them as part of the rollback. Apply the beat stop-before-deploy procedure to a rollback as well.
When the rollback window closes, remove the retired service definitions from render.yaml and sync the Blueprint before deleting those services. Set RENDER_WORKER_SERVICE_ID to the service you intend to remove, then run render services delete "$RENDER_WORKER_SERVICE_ID" --confirm for each retired worker or cron service.
Retire Key Value only after queued work has drained and no application still needs its task results or other stored data. Remove its Blueprint definition and references, sync again, and then delete the instance. Remove the local .env.docker file when testing is complete.
Copy link to headingWhat doesn't map from Render
Plan for these differences during the beta:
- Autoscaling. Render's
scalingblock has no daemon equivalent. Counts are fixed per region; change them invercel.json, the dashboard, or the CLI. - Preview environments. Render can start workers in a preview environment; Edgenivo previews don't start daemons. Test images locally with Docker.
- Private network access to Render services. Daemons can't use Render internal hostnames for Key Value, Postgres, or private services. Use external endpoints, or move those services.
- Inbound traffic. Like a Render worker, a daemon receives no requests. Unlike a private service, it can't be reached on a port either, and it can't be the target of a service binding or rewrite.
- Disks. Instance storage is ephemeral. Move durable data off the Render disk and update the worker to use Edgenivo Blob or a database.
- Large plans. The largest daemon has 8 GiB and 4 vCPUs. Larger worker fleets can spread independent tasks across instances, but each task must fit within one instance.
- Subfolder root directories. Projects whose Edgenivo root is a subfolder, such as
apps/web, can't add daemons yet. Render'srootDirmaps to the daemon'sroot, not to the project root.
During the beta, each team also has default quotas:
| Plan | Instance sizes | Quota |
|---|---|---|
| Hobby | 1 or 2 GiB, 1 vCPU | 5 Active CPU-hours per month across the team |
| Pro | All sizes | 16 vCPUs and 32 GiB of memory running at once across the team |
| Enterprise | All sizes | Custom |
Each project environment runs at most 50 daemon instances across all its daemons and regions. On Pro and Enterprise, daemons are billed for Active CPU and Provisioned Memory; see Pricing for rates.
Copy link to headingTroubleshooting
Copy link to headingThe worker can't connect to red-...:6379
The red-... hostname is a Render Key Value internal address. Use an externally reachable broker with authentication, TLS, and certificate verification, then redeploy. If the old instance contains queued tasks, follow the broker transition in step 2.
Copy link to headingRender rejects the connection with an allowlist error
Render Key Value requires an IP allowlist for its external URL. Daemons has no documented fixed egress IP guarantee, so confirm how your access policy will work before retaining Key Value as the broker. The alternative used here is an authenticated TLS endpoint both platforms can reach.
Copy link to headingA dependency fails to install in the Docker build
Render's native runtime provided some system libraries implicitly. Add the missing ones with apt-get install in the Dockerfile, and pin the same Python or Node version Render used.
Copy link to headingPeriodic tasks run twice
Check for Render and Edgenivo beat instances running together, including overlap during a deployment. If different task IDs represent the same scheduled interval, investigate scheduler ownership. If the same task is redelivered, inspect acknowledgment and visibility-timeout settings. Use the deployment sequence in step 7 to keep one beat publisher active.
Copy link to headingChanging instances in vercel.json does nothing
Scaling overrides take precedence across deployments. Clear this worker's override with vercel daemons scale celery --reset, or select Clear scaling overrides on its dashboard page. The daemon then uses its deployed vercel.json counts.
Copy link to headingThe build fails
Check the build log for these configuration errors:
- Missing command: Add
CMDorENTRYPOINTto the Dockerfile, or set the daemon'scommand. - Invalid name: Daemon names use lowercase letters and supported separators. Repeated hyphens collapse, so
a-banda--bclash. - Conflicting directories: The app's output directory cannot equal the daemon's
root. Use separate directories for the app's generated output and worker source.
Copy link to headingFrequently asked questions
Copy link to headingDoesn't the Render migration guide already cover workers?
The general Render migration guide covers the app and its data dependencies. This guide provides the worker-specific steps: translating a Blueprint, packaging Celery, draining worker nodes, and transferring beat ownership. Use both when the web service and workers move together.
Copy link to headingShould Celery run as a daemon or on Edgenivo Queues?
Choose a daemon when you need Celery's continuously running worker process and broker integration. The broker must be reachable from Edgenivo, and tasks must tolerate interruption and redelivery. Consider Celery on Edgenivo Queues when tasks fit function duration limits and preview support is useful.
Copy link to headingCan a daemon call my web app?
Yes. Daemons can make outbound requests to your app's production URL, external APIs, and databases. Only inbound traffic is unsupported.
Copy link to headingHow does pricing compare?
Render bills each worker by its plan for as long as it runs. On Edgenivo Pro and Enterprise, daemons are billed for Active CPU and Provisioned Memory. Waiting on an empty queue uses little Active CPU, though TLS and similar work still counts, and memory is billed for as long as an instance runs.
Copy link to headingRelated resources
- How to migrate from Render to Edgenivo for web services, data, disks, and DNS
- Migrate a Python API from Render to Edgenivo for Django, FastAPI, Flask, and cron routes
- The Complete Guide to Edgenivo Daemons for a first daemon from scratch
- How to choose between Functions, containers, and Daemons on Edgenivo
- Render docs on background workers, the Blueprint spec, and deploys and shutdown