BullMQ provides built-in metrics functionality that allows you to track the performance of your queues over time. Workers can count the number of jobs they process per minute and store this data in Redis for later analysis.
Enabling Metrics
Enable metrics collection on your workers by specifying how many data points to keep:
import { Worker , MetricsTime } from 'bullmq' ;
const worker = new Worker ( 'tasks' , async job => {
return await processJob ( job );
}, {
connection: {
host: 'localhost' ,
port: 6379 ,
},
metrics: {
maxDataPoints: MetricsTime . ONE_WEEK * 2 ,
},
});
Number of data points (minutes) to store. Each data point represents one minute of metrics.
All workers for the same queue must use the same metrics settings to get consistent data.
MetricsTime Constants
BullMQ provides convenient time constants:
import { MetricsTime } from 'bullmq' ;
// Available constants
MetricsTime . ONE_SECOND // 1 data point
MetricsTime . ONE_MINUTE // 1 data point
MetricsTime . ONE_HOUR // 60 data points
MetricsTime . ONE_DAY // 1,440 data points
MetricsTime . ONE_WEEK // 10,080 data points
MetricsTime . TWO_WEEKS // 20,160 data points
MetricsTime . ONE_MONTH // ~43,200 data points
Common Configurations
// Keep 1 hour of metrics
metrics : { maxDataPoints : MetricsTime . ONE_HOUR }
// Keep 1 day of metrics
metrics : { maxDataPoints : MetricsTime . ONE_DAY }
// Keep 2 weeks of metrics (recommended)
metrics : { maxDataPoints : MetricsTime . ONE_WEEK * 2 }
// Keep 1 month of metrics
metrics : { maxDataPoints : MetricsTime . ONE_MONTH }
Memory Usage
Metrics are stored efficiently in Redis:
Data points are 1-minute intervals
2 weeks of data ≈ 120 KB per queue
Older data is automatically removed
Memory usage remains constant after reaching max data points
// Example: 2 weeks of metrics
// 2 weeks = 14 days = 20,160 minutes = 20,160 data points
// Approximate memory: 120 KB
const worker = new Worker ( 'tasks' , processor , {
metrics: {
maxDataPoints: MetricsTime . ONE_WEEK * 2 , // ~120 KB
},
});
Querying Metrics
Retrieve metrics using the getMetrics method on the Queue class:
import { Queue , MetricsTime } from 'bullmq' ;
const queue = new Queue ( 'tasks' , {
connection: {
host: 'localhost' ,
port: 6379 ,
},
});
// Get completed job metrics for the last 2 weeks
const completedMetrics = await queue . getMetrics (
'completed' ,
0 ,
MetricsTime . ONE_WEEK * 2
);
// Get failed job metrics for the last 24 hours
const failedMetrics = await queue . getMetrics (
'failed' ,
0 ,
MetricsTime . ONE_DAY
);
Method Signature
async getMetrics (
type : 'completed' | 'failed' ,
start ?: number , // Default: 0
end ?: number // Default: -1 (all data)
): Promise < Metrics >
type
'completed' | 'failed'
required
The type of metrics to retrieve: completed for successful jobs or failed for failed jobs.
Starting index for pagination. Use 0 to start from the beginning.
Ending index for pagination. Use -1 to retrieve all data.
The getMetrics method returns a Metrics object:
interface Metrics {
data : number []; // Array of job counts per minute
count : number ; // Total count since queue started
meta : {
count : number ; // Same as count above
prevTS : number ; // Internal use - previous timestamp
prevCount : number ; // Internal use - previous count
};
}
Example Response
const metrics = await queue . getMetrics ( 'completed' , 0 , 60 );
console . log ( metrics );
// Output:
// {
// data: [5, 8, 3, 12, 7, 9, 11, ...], // Jobs per minute
// count: 15284, // Total jobs completed
// meta: {
// count: 15284,
// prevTS: 1678901234567,
// prevCount: 15279
// }
// }
Understanding the Data
data array:
Each element represents one minute
Value is the number of jobs completed (or failed) in that minute
Ordered from oldest to newest
count field:
Total number of jobs completed (or failed) since the queue started
Not limited to the queried time range
Useful for long-term tracking
meta fields:
Used internally by the metrics system
prevTS and prevCount should not be used in application code
Practical Examples
Example 1: Basic Metrics Tracking
import { Queue , Worker , MetricsTime } from 'bullmq' ;
// Enable metrics on worker
const worker = new Worker ( 'email' , async job => {
await sendEmail ( job . data );
}, {
metrics: {
maxDataPoints: MetricsTime . ONE_DAY ,
},
});
// Query metrics
const queue = new Queue ( 'email' );
setInterval ( async () => {
const metrics = await queue . getMetrics ( 'completed' , 0 , 60 );
// Calculate jobs per hour
const jobsLastHour = metrics . data . reduce (( sum , count ) => sum + count , 0 );
console . log ( `Jobs completed in last hour: ${ jobsLastHour } ` );
console . log ( `Total jobs completed: ${ metrics . count } ` );
}, 60000 ); // Check every minute
import { Queue , MetricsTime } from 'bullmq' ;
const queue = new Queue ( 'tasks' );
async function getDashboardStats () {
const completed = await queue . getMetrics ( 'completed' , 0 , MetricsTime . ONE_HOUR );
const failed = await queue . getMetrics ( 'failed' , 0 , MetricsTime . ONE_HOUR );
const completedLastHour = completed . data . reduce (( sum , n ) => sum + n , 0 );
const failedLastHour = failed . data . reduce (( sum , n ) => sum + n , 0 );
const totalProcessed = completedLastHour + failedLastHour ;
const successRate = totalProcessed > 0
? ( completedLastHour / totalProcessed * 100 ). toFixed ( 2 )
: 0 ;
return {
completedLastHour ,
failedLastHour ,
totalProcessed ,
successRate: ` ${ successRate } %` ,
allTimeCompleted: completed . count ,
allTimeFailed: failed . count ,
};
}
// Display dashboard every 5 minutes
setInterval ( async () => {
const stats = await getDashboardStats ();
console . log ( '=== Queue Dashboard ===' );
console . log ( `Completed (1h): ${ stats . completedLastHour } ` );
console . log ( `Failed (1h): ${ stats . failedLastHour } ` );
console . log ( `Success Rate: ${ stats . successRate } ` );
console . log ( `All-time: ${ stats . allTimeCompleted } completed, ${ stats . allTimeFailed } failed` );
}, 300000 );
Example 3: Alerting on Failures
import { Queue , MetricsTime } from 'bullmq' ;
const queue = new Queue ( 'critical-tasks' );
async function checkFailureRate () {
const failed = await queue . getMetrics ( 'failed' , 0 , 30 ); // Last 30 minutes
const completed = await queue . getMetrics ( 'completed' , 0 , 30 );
const recentFailed = failed . data . reduce (( sum , n ) => sum + n , 0 );
const recentCompleted = completed . data . reduce (( sum , n ) => sum + n , 0 );
const total = recentFailed + recentCompleted ;
if ( total > 0 ) {
const failureRate = ( recentFailed / total ) * 100 ;
if ( failureRate > 10 ) {
console . error ( `⚠️ HIGH FAILURE RATE: ${ failureRate . toFixed ( 2 ) } %` );
console . error ( `Failed: ${ recentFailed } , Completed: ${ recentCompleted } ` );
// Send alert
await alerting . send ({
level: 'warning' ,
message: `Queue failure rate is ${ failureRate . toFixed ( 2 ) } %` ,
details: { recentFailed , recentCompleted },
});
}
}
}
// Check every 5 minutes
setInterval ( checkFailureRate , 300000 );
Example 4: Paginated Metrics
import { Queue } from 'bullmq' ;
const queue = new Queue ( 'tasks' );
// Get first 60 minutes (1 hour)
const page1 = await queue . getMetrics ( 'completed' , 0 , 60 );
console . log ( 'First hour:' , page1 . data );
// Get next 60 minutes (2nd hour)
const page2 = await queue . getMetrics ( 'completed' , 60 , 120 );
console . log ( 'Second hour:' , page2 . data );
// Get last 60 minutes using negative index
const lastHour = await queue . getMetrics ( 'completed' , - 60 , - 1 );
console . log ( 'Last hour:' , lastHour . data );
Example 5: Calculating Throughput
import { Queue , MetricsTime } from 'bullmq' ;
const queue = new Queue ( 'tasks' );
async function calculateThroughput () {
const metrics = await queue . getMetrics ( 'completed' , 0 , MetricsTime . ONE_DAY );
// Jobs per minute (last hour)
const lastHourData = metrics . data . slice ( - 60 );
const avgPerMinute = lastHourData . reduce (( sum , n ) => sum + n , 0 ) / 60 ;
// Jobs per hour (last day)
const hoursInDay = 24 ;
const totalLastDay = metrics . data . reduce (( sum , n ) => sum + n , 0 );
const avgPerHour = totalLastDay / hoursInDay ;
return {
perMinute: avgPerMinute . toFixed ( 2 ),
perHour: avgPerHour . toFixed ( 2 ),
perDay: totalLastDay ,
};
}
const throughput = await calculateThroughput ();
console . log ( 'Average throughput:' );
console . log ( ` ${ throughput . perMinute } jobs/minute` );
console . log ( ` ${ throughput . perHour } jobs/hour` );
console . log ( ` ${ throughput . perDay } jobs/day` );
Prometheus Integration
For Prometheus metrics export, see Prometheus documentation.
import http from 'http' ;
import { Queue } from 'bullmq' ;
const queue = new Queue ( 'tasks' );
const server = http . createServer ( async ( req , res ) => {
if ( req . url === '/metrics' && req . method === 'GET' ) {
// Export Prometheus metrics
const metrics = await queue . exportPrometheusMetrics ();
res . writeHead ( 200 , { 'Content-Type' : 'text/plain' });
res . end ( metrics );
} else {
res . writeHead ( 404 );
res . end ( 'Not Found' );
}
});
server . listen ( 3000 );
console . log ( 'Prometheus metrics available at http://localhost:3000/metrics' );
Best Practices
Use consistent settings across workers
All workers for the same queue must have identical metrics configuration.
Start with 2 weeks of data
This provides good balance between memory usage (~120 KB) and historical data.
Monitor both completed and failed metrics
Track success rate by comparing completed vs failed job counts.
Set up alerts for anomalies
Monitor failure rates, throughput drops, or unusual patterns.
Use pagination for large datasets
When querying long time ranges, use pagination to avoid loading too much data.
Combine with external monitoring
Integrate with Prometheus, Grafana, or other monitoring tools for visualization.
Limitations
Metrics are aggregated in 1-minute intervals . You cannot get sub-minute granularity.
The count field shows all-time totals , not just the queried time range.
Metrics data is stored in Redis and will be lost if Redis is flushed or data is not persisted.
Telemetry OpenTelemetry integration for tracing
Queue Events Listen to job events in real-time
Workers Overview Configure and manage workers
Going to Production Production deployment best practices
API Reference