Implement Node.js application monitoring with Prometheus metrics collection and Grafana dashboards

Intermediate 45 min May 26, 2026 86 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up comprehensive Node.js application monitoring with Prometheus metrics collection, custom performance tracking, and Grafana dashboards for real-time observability and alerting.

Prerequisites

  • Node.js application running
  • Sudo access
  • Port 3000, 9090, 9093 available

What this solves

Monitoring Node.js applications in production requires visibility into application performance, request rates, error counts, and resource usage. This tutorial sets up Prometheus metrics collection for Node.js applications and creates comprehensive Grafana dashboards for monitoring application health, performance bottlenecks, and setting up automated alerts.

Prerequisites

You'll need a Node.js application running on your server and sudo access to install monitoring components. This tutorial assumes you have a basic Express.js application, but the metrics collection patterns work with any Node.js framework.

Step-by-step configuration

Install Prometheus server

Download and install Prometheus to collect metrics from your Node.js applications.

cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.45.0/prometheus-2.45.0.linux-amd64.tar.gz
tar xzf prometheus-2.45.0.linux-amd64.tar.gz
sudo cp prometheus-2.45.0.linux-amd64/prometheus /usr/local/bin/
sudo cp prometheus-2.45.0.linux-amd64/promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo cp -r prometheus-2.45.0.linux-amd64/consoles /etc/prometheus
sudo cp -r prometheus-2.45.0.linux-amd64/console_libraries /etc/prometheus
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.45.0/prometheus-2.45.0.linux-amd64.tar.gz
tar xzf prometheus-2.45.0.linux-amd64.tar.gz
sudo cp prometheus-2.45.0.linux-amd64/prometheus /usr/local/bin/
sudo cp prometheus-2.45.0.linux-amd64/promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo cp -r prometheus-2.45.0.linux-amd64/consoles /etc/prometheus
sudo cp -r prometheus-2.45.0.linux-amd64/console_libraries /etc/prometheus

Create Prometheus user and set permissions

Create a dedicated user for Prometheus and set correct ownership on configuration directories.

sudo useradd --no-create-home --shell /bin/false prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool

Configure Prometheus

Create the main Prometheus configuration file to scrape Node.js application metrics.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "/etc/prometheus/rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - localhost:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node-app'
    static_configs:
      - targets: ['localhost:3000']
    metrics_path: '/metrics'
    scrape_interval: 5s

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['localhost:9100']

Create Prometheus systemd service

Set up Prometheus to run as a system service with automatic startup.

[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
    --config.file /etc/prometheus/prometheus.yml \
    --storage.tsdb.path /var/lib/prometheus/ \
    --web.console.templates=/etc/prometheus/consoles \
    --web.console.libraries=/etc/prometheus/console_libraries \
    --web.listen-address=0.0.0.0:9090 \
    --web.enable-lifecycle

[Install]
WantedBy=multi-user.target

Install Node Exporter

Install Node Exporter to collect system metrics alongside application metrics.

cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
tar xzf node_exporter-1.6.1.linux-amd64.tar.gz
sudo cp node_exporter-1.6.1.linux-amd64/node_exporter /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/node_exporter

Create Node Exporter systemd service

Configure Node Exporter as a system service for system metrics collection.

[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/node_exporter

[Install]
WantedBy=multi-user.target

Install Prometheus client in Node.js application

Add the Prometheus client library to your Node.js application for metrics collection.

cd /path/to/your/node/app
npm install prom-client express-prometheus-middleware

Configure Node.js application metrics

Add Prometheus metrics collection to your Express.js application with custom performance tracking.

const express = require('express');
const client = require('prom-client');
const promMid = require('express-prometheus-middleware');

const app = express();

// Create a Registry which registers the metrics
const register = new client.Registry();

// Add a default label which is added to all metrics
register.setDefaultLabels({
  app: 'my-node-app'
});

// Enable the collection of default metrics
client.collectDefaultMetrics({ register });

// Custom metrics
const httpRequestDuration = new client.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status'],
  buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10]
});

const httpRequestsTotal = new client.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status']
});

const activeConnections = new client.Gauge({
  name: 'active_connections',
  help: 'Number of active connections'
});

const databaseConnections = new client.Gauge({
  name: 'database_connections',
  help: 'Number of database connections',
  labelNames: ['database', 'state']
});

// Register custom metrics
register.registerMetric(httpRequestDuration);
register.registerMetric(httpRequestsTotal);
register.registerMetric(activeConnections);
register.registerMetric(databaseConnections);

// Middleware to track request metrics
app.use((req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = (Date.now() - start) / 1000;
    const route = req.route ? req.route.path : req.url;
    
    httpRequestDuration
      .labels(req.method, route, res.statusCode)
      .observe(duration);
    
    httpRequestsTotal
      .labels(req.method, route, res.statusCode)
      .inc();
  });
  
  next();
});

// Track active connections
app.use((req, res, next) => {
  activeConnections.inc();
  res.on('finish', () => activeConnections.dec());
  next();
});

// Metrics endpoint
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

// Health check endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});

// Example route with custom metrics
app.get('/api/users', (req, res) => {
  // Simulate database connection tracking
  databaseConnections.labels('users_db', 'active').set(5);
  databaseConnections.labels('users_db', 'idle').set(3);
  
  res.json({ users: ['alice', 'bob', 'charlie'] });
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(Server running on port ${port});
  console.log(Metrics available at http://localhost:${port}/metrics);
});

Install and configure Grafana

Install Grafana for creating dashboards and visualizing Prometheus metrics.

sudo apt update
sudo apt install -y software-properties-common
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
sudo dnf install -y https://dl.grafana.com/oss/release/grafana-10.1.1-1.x86_64.rpm

Start and enable services

Start all monitoring services and enable them to start automatically on boot.

sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl enable --now node_exporter
sudo systemctl enable --now grafana-server

Verify services are running

sudo systemctl status prometheus sudo systemctl status node_exporter sudo systemctl status grafana-server

Configure Grafana data source

Add Prometheus as a data source in Grafana for metrics visualization.

# Access Grafana web interface at http://your-server:3000

Default credentials: admin/admin

Add Prometheus data source via API

curl -X POST \ http://admin:admin@localhost:3000/api/datasources \ -H 'Content-Type: application/json' \ -d '{ "name": "Prometheus", "type": "prometheus", "url": "http://localhost:9090", "access": "proxy", "isDefault": true }'

Create Node.js application dashboard

Import a comprehensive dashboard for Node.js application monitoring.

{
  "dashboard": {
    "id": null,
    "title": "Node.js Application Monitoring",
    "tags": ["nodejs", "prometheus"],
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "HTTP Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])",
            "legendFormat": "{{method}} {{route}}"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0}
      },
      {
        "id": 2,
        "title": "HTTP Request Duration",
        "type": "graph",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "95th percentile"
          },
          {
            "expr": "histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "50th percentile"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 0}
      },
      {
        "id": 3,
        "title": "Active Connections",
        "type": "singlestat",
        "targets": [
          {
            "expr": "active_connections",
            "legendFormat": "Connections"
          }
        ],
        "gridPos": {"h": 4, "w": 6, "x": 0, "y": 8}
      },
      {
        "id": 4,
        "title": "Memory Usage",
        "type": "graph",
        "targets": [
          {
            "expr": "process_resident_memory_bytes",
            "legendFormat": "RSS Memory"
          },
          {
            "expr": "nodejs_heap_size_used_bytes",
            "legendFormat": "Heap Used"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 6, "y": 8}
      }
    ],
    "time": {"from": "now-1h", "to": "now"},
    "refresh": "5s"
  }
}
# Import dashboard via API
curl -X POST \
  http://admin:admin@localhost:3000/api/dashboards/db \
  -H 'Content-Type: application/json' \
  -d @/tmp/nodejs-dashboard.json

Configure alerting rules

Set up Prometheus alerting rules for Node.js application monitoring.

sudo mkdir -p /etc/prometheus/rules
groups:
  - name: nodejs_alerts
    rules:
      - alert: HighRequestLatency
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High request latency on Node.js application"
          description: "95th percentile latency is {{ $value }}s for more than 5 minutes"
      
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "High error rate on Node.js application"
          description: "Error rate is {{ $value }} errors per second"
      
      - alert: MemoryUsageHigh
        expr: process_resident_memory_bytes > 500000000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage on Node.js application"
          description: "Memory usage is {{ $value | humanize }}B"
      
      - alert: NodeJSDown
        expr: up{job="node-app"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Node.js application is down"
          description: "Node.js application has been down for more than 1 minute"

Install and configure Alertmanager

Set up Alertmanager for handling and routing alerts from Prometheus.

cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.25.0/alertmanager-0.25.0.linux-amd64.tar.gz
tar xzf alertmanager-0.25.0.linux-amd64.tar.gz
sudo cp alertmanager-0.25.0.linux-amd64/alertmanager /usr/local/bin/
sudo cp alertmanager-0.25.0.linux-amd64/amtool /usr/local/bin/
sudo mkdir -p /etc/alertmanager
sudo chown prometheus:prometheus /usr/local/bin/alertmanager /usr/local/bin/amtool /etc/alertmanager

Configure Alertmanager

Set up Alertmanager configuration for email and webhook notifications.

global:
  smtp_smarthost: 'localhost:587'
  smtp_from: 'alerts@example.com'
  smtp_auth_username: 'alerts@example.com'
  smtp_auth_password: 'your_email_password'

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'web.hook'

receivers:
  - name: 'web.hook'
    email_configs:
      - to: 'admin@example.com'
        subject: '[ALERT] {{ .GroupLabels.alertname }}'
        body: |
          {{ range .Alerts }}
          Alert: {{ .Annotations.summary }}
          Description: {{ .Annotations.description }}
          Labels: {{ range .Labels.SortedPairs }}{{ .Name }}={{ .Value }} {{ end }}
          {{ end }}
    
inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'dev', 'instance']

Create Alertmanager systemd service

Configure Alertmanager to run as a system service.

[Unit]
Description=Alertmanager
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/alertmanager \
    --config.file=/etc/alertmanager/alertmanager.yml \
    --storage.path=/var/lib/alertmanager/

[Install]
WantedBy=multi-user.target

Set up firewall rules

Configure firewall rules to allow access to monitoring services.

sudo ufw allow 9090/tcp comment 'Prometheus'
sudo ufw allow 3000/tcp comment 'Grafana'
sudo ufw allow 9093/tcp comment 'Alertmanager'
sudo ufw reload
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --permanent --add-port=9093/tcp
sudo firewall-cmd --reload

Start Alertmanager and restart Prometheus

Enable Alertmanager and restart Prometheus to load the new alerting rules.

sudo mkdir -p /var/lib/alertmanager
sudo chown prometheus:prometheus /var/lib/alertmanager
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager
sudo systemctl restart prometheus

Verify services

sudo systemctl status alertmanager sudo systemctl status prometheus

Verify your setup

Check that all monitoring components are working correctly and collecting metrics.

# Check Prometheus is collecting metrics
curl http://localhost:9090/api/v1/targets

Check Node.js application metrics

curl http://localhost:3000/metrics | grep http_requests_total

Check Grafana is accessible

curl -I http://localhost:3000/login

Check Alertmanager is running

curl http://localhost:9093/-/healthy

Test alert rules

prometheus --config.file=/etc/prometheus/prometheus.yml --web.listen-address=:9091 --dry-run

Access your monitoring stack:

  • Prometheus: http://your-server:9090
  • Grafana: http://your-server:3000 (admin/admin)
  • Alertmanager: http://your-server:9093
  • Node.js metrics: http://your-server:3000/metrics

Common issues

SymptomCauseFix
Prometheus can't scrape Node.js metricsWrong port or path configurationCheck scrape_configs in /etc/prometheus/prometheus.yml matches your app port
Grafana shows "No data"Prometheus data source not configuredAdd Prometheus as data source at http://localhost:9090
Alerts not firingAlert rules syntax errorRun promtool check rules /etc/prometheus/rules/*.yml
High memory usageToo many metrics retainedConfigure retention in Prometheus with --storage.tsdb.retention.time=15d
Node Exporter permission deniedIncorrect user permissionsEnsure prometheus user owns /usr/local/bin/node_exporter
Alertmanager not sending emailsSMTP configuration errorTest SMTP settings and check /etc/alertmanager/alertmanager.yml
Never use chmod 777. If you encounter permission issues with Prometheus data directories, use sudo chown -R prometheus:prometheus /var/lib/prometheus and chmod 755 for directories, chmod 644 for files.

Next steps

Expand your monitoring setup with these advanced configurations:

Running this in production?

Want this handled for you? Setting up monitoring once is straightforward. Keeping it patched, tuned, and handling incident response across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.