Background processes

Processes → Background processes manages long-running commands that belong to an app - a queue worker (Celery, ARQ, Dramatiq, RQ, TaskIQ), a scheduler daemon, a consumer, a bot. Each one runs as its own systemd service: supervised, restarted on crash, restarted on every deploy so it always runs your latest code.

(For commands that run and then exit on a schedule - a nightly cleanup, a report - use the Scheduler instead, on the tab next to this one.)

There's no tool lock-in and nothing framework-specific to configure. A process is just a command we keep alive; what it is lives in the command, not a setting.

Adding a process

  1. Processes → Background processes → Add background process.
  2. Give it a name and the command. Bare tool names (celery, arq, rq, dramatiq) resolve in the app's virtualenv, and the working directory is your project folder - so commands read the same as they would over SSH.
  3. Optionally set a graceful shutdown timeout (see Safe restarts).

The form offers example commands based on your stack - async apps (FastAPI, or Django on ASGI) get the async-native queues first, sync apps get Celery and RQ first. They're click-to-fill starting points; edit them freely. Deeper per-tool setup lives in each tool's own docs.

Pointing a worker at Redis

Your server already runs Redis, and most queue tools want a broker. Two ways to hand it over:

  • On the command line - drop the $REDIS_URL token into the command and Pyvolt substitutes the box's Redis (with a per-app database index) when it writes the service. For tools that take the broker as a flag:

rq worker --url $REDIS_URL high default celery -A myproject worker --broker $REDIS_URL --loglevel info

  • From your config - if your tool reads the broker in code (ARQ's WorkerSettings, or Celery configured in settings.py), a command-line token can't reach it. Instead set a REDIS_URL environment variable on the Environment tab to the value shown in the add-process form, and read it in your app.

The same substitution covers $SOCK, $SOCK_PATH and $APP_MODULE, matching the tokens you can use in a serve command.

Schedulers and periodic tasks

How you run periodic work depends on what you need:

  • No queue, just run a command on a schedule (a management command, a cleanup, a report) - use the Scheduler, on the tab next to this one. It runs any command on a cron schedule and captures its output; nothing to keep alive.
  • Worker-embedded schedulers (ARQ, SAQ) run cron inside the worker itself - there's nothing extra to add here; declare the schedule in your code and the worker handles it.
  • Celery beat is a separate long-running daemon, so it's just another background process. Add one with:

celery -A myproject beat --loglevel info

Beat must be a singleton - two schedulers double-fire every periodic task - so add exactly one. Each process is a single systemd unit, so one beat process is one scheduler; just don't add a second.

For those code-declared schedulers there's nothing to administer in Pyvolt: the process runs whatever schedule your code declares (Celery's app.conf.beat_schedule / CELERY_BEAT_SCHEDULE, ARQ's cron jobs, and so on). Edit the schedule in your repo and push - deploys restart the process, so changes apply automatically. Only the Scheduler is administered in Pyvolt, where you set the cron expression per job.

Any command works

Beyond queues, anything long-running is fair game: a message consumer, a websocket bridge, a chat bot, a polling loop. Name it, paste the command, and it becomes a supervised service like the rest.

Status, honestly

Each row shows the real systemd state - running, stopped, starting, or crash-loop with the exit code when a process keeps dying. No guessing about causes: click the log icon to see the process's actual journal output (the dialog opens instantly and loads the last 80 lines).

Check process state from your terminal (the CLI reference also covers tailing a process's log and restarting one):

CLI
pyvolt ps myapp.com

Safe restarts

Stopping or restarting sends SIGTERM first - for a well-behaved worker that's a warm shutdown (Celery, for instance, finishes in-flight tasks and requeues the rest). The stop timeout (default 75s, up to 600s) is how long a process gets before it's force-killed; raise it for workers that chew long tasks.

Deploys restart everything

A successful deploy restarts the app and all its processes - workers never keep running stale code.

View this page as markdown - handy as context for your AI tools. Full index at /llms.txt.