Skip to main content

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-bossBullMQ
Backing storePostgreSQLRedis
Extra infra neededNone (reuses your DB)Redis instance
Job visibilityStored in pgboss schema — queryable with SQLStored in Redis keys
Best forSimpler setups, fewer moving partsHigh-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():

ChannelTriggered by
SEND_MAILAny email send (verification, magic link, billing alerts) — retries 3× with 60s delay on failure
SEND_WEBHOOKAccount webhook event fired
SEND_SLACKServer 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.