Skip to content
Docs

Daemons

Daemons are available in Public Beta on all plans

Edgenivo Daemons run long-running background processes from container images, such as queue consumers, workflow workers, and job runners. Each daemon pulls work from a queue, workflow engine, or external API and processes it continuously.

Edgenivo keeps your configured number of instances running in each region and restarts instances that exit or crash. Daemons run on Fluid compute, the same compute platform as Edgenivo Functions.

Daemons are available on all plans, including Hobby, within each plan's quotas. Hobby has monthly quotas for Active CPU and Provisioned Memory. A daemon that is mostly waiting for work uses less Active CPU, but it uses Provisioned Memory for every hour it runs. See Default quotas.

Use a daemon when your process owns a continuous loop to retrieve work. Common examples include:

  • Queue consumers that poll Edgenivo Queues, Amazon SQS, Kafka, or Redis, including workers for Celery, Sidekiq, and BullMQ.
  • Workflow workers that retrieve tasks from a workflow engine.
  • Job runners, such as self-hosted CI runners that wait for jobs.

Daemons complement other primitives, such as Edgenivo Functions to serve HTTP traffic, Edgenivo Workflows for durable execution, and Edgenivo Queues for serverless message processing with preview deployment support. Use Cron Jobs to run code on a schedule, Edgenivo Sandbox to run untrusted code in isolation, and Services to define multiple apps in a single project.

A daemon is a named resource in your project that runs one or more instances of the same workload. Each instance is an identical, interchangeable copy of the process:

  1. Build: During the build, Edgenivo builds each daemon's Dockerfile or Containerfile and pushes the image to Edgenivo Container Registry as a repository named daemon.<name>.
  2. Deploy: When a deployment becomes your production deployment, including after a promotion or rollback, Edgenivo rolls out its configured daemons in each region while gracefully terminating instances from the previous deployment.
  3. Run: Your daemon runs continuously. Edgenivo forwards each instance's stdout and stderr output to your project's Logs in the dashboard.
  4. Retry: Edgenivo maintains the requested instance counts and restarts instances that exit or crash.
  5. Scale: You can override a daemon's instance counts without a new deployment. Overrides stay in place across deployments until you clear them. See Scaling a daemon.
  6. Stop: When it's time to stop your process, such as during a rollout or a manual restart, Edgenivo sends a SIGTERM signal. Your process has time to clean up resources before Edgenivo terminates it. See Graceful shutdown.

During the beta phase, daemons only run in production environments. Daemons in preview deployments are not supported.

Package a long-running Node.js process as a daemon and deploy two instances to iad1 (Washington, D.C., USA).

Daemons use Dockerfile syntax to package code. In your worker's source directory, add a Dockerfile that builds a runnable version of your application:

worker/Dockerfile
FROM node:24-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "worker.js"]

Then, declare a daemon using the daemons key in your project configuration:

vercel.json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "daemons": {
    "worker": {
      "root": "worker/",
      "entrypoint": "Dockerfile",
      "instances": { "iad1": 2 }
    }
  }
}
vercel.toml
[daemons.worker]
root = "worker/"
entrypoint = "Dockerfile"
instances = { iad1 = 2 }

Deploy your project to production. Edgenivo will roll out two instances of worker to iad1, and their output will appear in your project's Logs.

Daemons run alongside your existing app without changing how Edgenivo builds it. For example, you can use Services to run a Next.js frontend alongside a FastAPI Python backend, then add BullMQ and Celery workers to consume work from each app.

Define a Dockerfile for each worker in workers/bullmq-worker/ and workers/celery-worker/, then add both daemons under the top-level daemons key in your project configuration:

vercel.json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "services": {
    "web": { "root": "web/" },
    "api": { "root": "api/", "entrypoint": "main:app" }
  },
  "rewrites": [
    { "source": "/api/(.*)", "destination": { "service": "api" } },
    { "source": "/(.*)", "destination": { "service": "web" } }
  ],
  "daemons": {
    "bullmq-worker": {
      "root": "workers/bullmq-worker/",
      "entrypoint": "Dockerfile",
      "instances": { "iad1": 1 }
    },
    "celery-worker": {
      "root": "workers/celery-worker/",
      "entrypoint": "Dockerfile",
      "instances": { "iad1": 1 }
    }
  }
}
vercel.toml
[services.web]
root = "web/"
 
[services.api]
root = "api/"
entrypoint = "main:app"
 
[[rewrites]]
source = "/api/(.*)"
destination = { service = "api" }
 
[[rewrites]]
source = "/(.*)"
destination = { service = "web" }
 
[daemons.bullmq-worker]
root = "workers/bullmq-worker/"
entrypoint = "Dockerfile"
instances = { iad1 = 1 }
 
[daemons.celery-worker]
root = "workers/celery-worker/"
entrypoint = "Dockerfile"
instances = { iad1 = 1 }

See the Daemon configuration reference for every field.

As a new version of your daemon rolls out, Edgenivo stops instances from the previous version. To let your process finish current tasks and clean up resources, Edgenivo sends a SIGTERM signal. Stop accepting new work immediately and start draining resources. By default, Edgenivo waits 30 seconds after sending SIGTERM, then terminates the process if it's still running. The same shutdown sequence applies when you reduce instance counts or remove a daemon.

To change how long Edgenivo waits, set terminationGracePeriodSeconds on the daemon. The value is an integer number of seconds from 0 to 3600:

vercel.json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "daemons": {
    "worker": {
      "root": "worker/",
      "entrypoint": "Dockerfile",
      "instances": { "iad1": 1 },
      "terminationGracePeriodSeconds": 50
    }
  }
}
vercel.toml
[daemons.worker]
root = "worker/"
entrypoint = "Dockerfile"
instances = { iad1 = 1 }
terminationGracePeriodSeconds = 50

An instance can stop at any time, so make jobs idempotent and interrupted jobs safe to retry. An idempotent job produces the same result if it runs more than once. For example, acknowledge a queue message only after you finish processing it.

Handle SIGTERM to stop taking new work and finish the work in progress:

worker/worker.ts
const shutdown = new AbortController();
 
process.on('SIGTERM', () => shutdown.abort());
 
async function processNextJob(signal: AbortSignal): Promise<void> {
  // Wait for one unit of work, for example with a long-polling queue
  // receive that stops when `signal` aborts, then process it. Back off
  // when there's no work so the loop doesn't spin.
}
 
async function main(): Promise<void> {
  while (!shutdown.signal.aborted) {
    await processNextJob(shutdown.signal);
  }
  process.exit(0);
}
 
void main();
worker/worker.py
import signal
from threading import Event
 
shutdown = Event()
 
signal.signal(signal.SIGTERM, lambda signum, frame: shutdown.set())
 
 
def process_next_job(shutdown: Event) -> None:
    # Replace this wait with a bounded queue poll, then process the job.
    # Finish processing before returning, even if shutdown is requested.
    shutdown.wait(timeout=1)
 
 
while not shutdown.is_set():
    process_next_job(shutdown)
 
# Close queue connections and other resources before exiting.

Each instance receives your deployment's environment variables. Edgenivo passes the same variables to the image build as build arguments.

Edgenivo also sets the following environment variables on each instance:

VariableDescriptionExample
VERCEL_REGIONThe region the instance runs in. Edgenivo ignores a VERCEL_REGION that you define.iad1
VERCEL_OIDC_TOKEN_FILEThe path of the file that holds the instance's OIDC token./var/run/secrets/vercel.com/token

Use the OIDC token to authenticate with other services. The token is valid for 30 minutes, and Edgenivo refreshes the file about every 5 minutes, so read the file each time you need a token. To read the token, use @vercel/oidc 4.0.0 or later for Node.js, or vercel-oidc 0.10.0 or later for Python.

vercel dev currently doesn't run daemons. When your configuration contains daemons, vercel dev prints a warning and starts the rest of your project. Run your daemon locally with Docker:

Terminal
docker build -t worker worker/
docker run --env-file .env.local worker

Use vercel env pull to download your project's development environment variables into .env.local.

During the beta, daemons run in production deployments with manually configured instance counts and ephemeral storage. You can scale daemons from the Edgenivo dashboard or the Edgenivo CLI.

Preview deployments, autoscaling, and persistent storage aren't supported. See Daemons limits for all limits and available alternatives.

The container image, command, and environment variables are part of the deployment. To change them, create a new production deployment. The command field replaces the image's ENTRYPOINT and CMD. See Managing daemons.

Deploy new instances counts or set a scaling override without a new deployment. Overrides stay in place across deployments until you clear them. See Scaling a daemon.

It uses the Provisioned Memory quota, and it uses the Active CPU quota only while your code executes. The Active CPU quota counts the CPU time your code spends executing, not uptime. Time spent waiting, such as long polling an empty queue, doesn't count, so a daemon that is mostly waiting for work will use less of your monthly Active CPU quota. Some work that looks like waiting still uses CPU: for example, HTTPS requests require cipher operations such as encryption, decryption, and signing, which consume Active CPU. The Provisioned Memory quota counts the memory of each instance for every hour it runs. See Default quotas for each plan's quota and what happens when you reach it.

Yes. During the beta, set VERCEL_ENABLE_DAEMONS_BOA=1 when you run vercel build locally.

Follow these guides to deploy background workers or migrate them from another platform:


Was this helpful?

supported.