Background Jobs
Prime runs scheduled and event-driven background jobs for tasks like sending emails, checking account expiry, and cleaning up media. The job queue system is pluggable — you can choose between BullMQ (Redis-backed) or pg-boss (PostgreSQL-backed) depending on your infrastructure.
Choosing a driver
Set the driver in demo-server/.env:
QUEUE_DRIVER=pgboss # default — uses your existing PostgreSQL database
# or
QUEUE_DRIVER=bullmq # uses Redis (requires Redis to be running)
| pg-boss | BullMQ | |
|---|---|---|
| Backing store | PostgreSQL | Redis |
| Extra infra needed | None (reuses your DB) | Redis instance |
| Job visibility | Stored in pgboss schema — queryable with SQL | Stored in Redis keys |
| Best for | Simpler setups, fewer moving parts | High-throughput, lower latency |
pg-boss is the default because it requires no extra infrastructure — it creates its own schema in your existing PostgreSQL database. Switch to BullMQ if you already have Redis running (e.g. for caching) and want faster queue processing.
pg-boss setup
pg-boss connects using the same database credentials as the rest of the app. No extra configuration needed beyond your standard DB_* env vars:
DB_HOST=localhost
DB_NAME=primesaas
DB_USER=primesaas
DB_PASSWORD=yourpassword
DB_PORT=5432
On first startup, pg-boss automatically creates a pgboss schema in your database with the tables it needs to track jobs.
BullMQ setup
BullMQ requires Redis. See Redis Setup for installation. Then add to demo-server/.env:
QUEUE_DRIVER=bullmq
REDIS_HOST=localhost
REDIS_PORT=6379
# REDIS_PASSWORD=yourpassword # if Redis auth is enabled
Scheduled jobs (cron)
All recurring jobs are configured via cron expressions in demo-server/.env. The default is midnight UTC (0 0 * * *) for all jobs:
# Check and mark accounts as expired
CHECK_EXPIRED_ACCOUNTS_CRON=0 0 * * *
# Clean up trial accounts past the cleanup window
ACCOUNT_TRIAL_EXCEEDED_CRON=0 0 * * *
# Reset billing cycle counters
REFRESH_BILLING_CYCLES_CRON=0 0 * * *
# Send expiry warning emails to accounts approaching expiry
EXPIRING_ACCOUNT_REMINDER_CRON=0 0 * * *
# Remove orphaned media files from S3
CLEANUP_UNUSED_MEDIA_CRON=0 0 * * *
# Scan uploaded media with AWS Rekognition
MODERATE_MEDIA_CRON=0 0 * * *
# Check database consistency
CHECK_DB_CONSISTENCY_CRON=0 0 * * *
Standard cron syntax: minute hour day month weekday. All jobs run in Etc/GMT.
Event-driven jobs
Beyond scheduled crons, some jobs are triggered by application events and dispatched immediately via JobQueue.publish():
| Channel | Triggered by |
|---|---|
SEND_MAIL | Any email send (verification, magic link, billing alerts) — retries 3× with 60s delay on failure |
SEND_WEBHOOK | Account webhook event fired |
SEND_SLACK | Server error, payment event, or policy violation |
Account expiry settings
These env vars control when accounts are cleaned up after expiry:
# Days before expiry to send a warning email
ACCOUNT_EXPIRY_REMINDER_DAYS=14
# Days after expiry before the account is permanently closed
ACCOUNT_CLEANUP_AFTER_DAYS=30
How it works internally
Both drivers expose the same JobQueue interface — a static class with init() and publish() methods. The active driver is resolved at startup via queue/index.js:
// demo-server/queue/index.js
const driver = process.env.QUEUE_DRIVER || "pgboss";
module.exports = driver === "bullmq"
? require("./bullmq")
: require("./pgboss");
Switching drivers requires only changing the QUEUE_DRIVER env var — no code changes needed.