DEV Community

Cover image for Monitoring for Express.js Applications Using Prometheus and Node Exporter
Obaseki Noruwa
Obaseki Noruwa

Posted on

Monitoring for Express.js Applications Using Prometheus and Node Exporter

Monitoring isn't just about knowing when things break, it's about understanding how your systems behave under real-world conditions and making data-driven decisions before issues impact users. In this comprehensive guide, I will walk you through building a robust monitoring solution for Express.js applications using the industry standard Prometheus ecosystem.

Why Monitoring Matters More Than You Think

During my journey into DevOps practices, I discovered that monitoring is often treated as an afterthought after CI/CD pipeline deployment. Teams build amazing applications, deploy them to production, and then set up monitoring as a reactive measure. However, I believe that as part of the journey to become a DevOps engineer, after learning containerization, the next critical step should be gaining hands-on experience in setting up monitoring with open-source toolkits for system optimization.

This approach transforms monitoring from a reactive necessity into a proactive strategy that enables better system understanding and performance optimization.

The Monitoring Stack Architecture

I chose the Prometheus ecosystem because it's become the de facto standard for container native monitoring. Here's why this combination works so well:

Prometheus: The Storage for Time-Series Data

Prometheus serves as the storage layer for time-series data, exposing its metrics endpoint at http://localhost:9090/metrics to be scraped by monitoring tools. The time-series database design excels at storing metrics with timestamps, enabling powerful queries and aggregations. When you need to understand how your application performed during last week's traffic spike, Prometheus gives you that historical context.

In your compose.monitoring.yml file, you can configure data retention in the command section by adding --storage.tsdb.retention.time=30d to maintain 30 days of historical data for trend analysis.

Node Exporter: The Metrics Collector

Node Exporter bridges the gap between application metrics and infrastructure health. It collects and exposes hardware statistics—CPU usage, memory consumption, disk I/O, and network activity—in Prometheus format. This integration means you can correlate application slowdowns with system resource constraints, making troubleshooting significantly more effective.

The collector operates by reading system files and translating them into Prometheus-compatible metrics, providing comprehensive visibility into your infrastructure's health.

Grafana: From Data to Insights

Raw metrics are meaningless without proper visualization. Grafana transforms Prometheus data into meaningful dashboards that tell the story of your application's health. The ability to create alerts based on metric thresholds means your monitoring system becomes proactive rather than reactive.

With Grafana's extensive dashboard library, you can leverage pre-built visualizations or create custom dashboards tailored to your specific monitoring needs.

Implementation

1. Repository Setup

# Clone the project repository
git clone https://github.com/noruwa03/express-js-nginx-lb
cd express-js-nginx-lb
Enter fullscreen mode Exit fullscreen mode

2. Application Configuration

# Navigate to application directory
cd app

# Install Node.js dependencies
npm install
Enter fullscreen mode Exit fullscreen mode

3. Environment Configuration

Create a .env file in the app/ directory with the following variables:

# Port
PORT=8080

# Database Configuration for Neon DB or AWS RDS
DB_CONN_LINK=postgresql://username:password@host:port/database
Enter fullscreen mode Exit fullscreen mode

4. Local Development (Optional)

# Start development server with hot reload to test
npm run dev
Enter fullscreen mode Exit fullscreen mode

5. Production Deployment

# Return to project root
cd ..

# Add name to compose.yml
name: monitoring-system

# Change ports of nginx service in compose.yml from 3000:80 to 4000:80, port 3000 will be used by Grafana service
nginx:
   ports:
      - 4000:80
nginx:
   ports:
      - 4000:80

# Add include option for compose.monitoring.yml
include:
  - compose.monitoring.yml

Enter fullscreen mode Exit fullscreen mode

Create compose.monitoring.yml file

services:
  node-exporter:
    image: prom/node-exporter
    container_name: node-exporter
    command:
      - --path.procfs=/host/proc
      - --path.rootfs=/rootfs
      - --path.sysfs=/host/sys
      - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc|run/user)($|/)
      - --collector.filesystem.fs-types-exclude=^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|iso9660|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    ports:
      - 9100:9100
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus
    container_name: prometheus
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - 9090:9090
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      # - --storage.tsdb.retention.time=30d
    networks:
      - monitoring
    depends_on:
      - node-exporter

  grafana:
    image: grafana/grafana
    container_name: grafana
    ports: 
      - 3000:3000
    networks:
      - monitoring
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_SERVER_ROOT_URL=http://localhost:3000/grafana/
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    volumes:
      - grafana-storage:/var/lib/grafana
    depends_on:
      - prometheus


networks:
  monitoring:
    driver: bridge

volumes:
  grafana-storage:

Enter fullscreen mode Exit fullscreen mode

Add monitoring directory and create monitoring.yml file


global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: node-exporter
    static_configs:
      - targets:
        - node-exporter:9100

  - job_name: prometheus
    static_configs:
      - targets:
        - prometheus:9090

Enter fullscreen mode Exit fullscreen mode

Architecture Overview

Application Structure (Before Monitoring)

express-js-nginx-lb/
├── app/                          # [Unchanged] app source code
├── nginx/                        # [Unchanged] Load balancer config
├── compose.yml                   # [Updated] Core application services
└── README.md
Enter fullscreen mode Exit fullscreen mode

Enhanced Structure (With Monitoring Stack)

express-js-nginx-lb/
├── app/                          # [Updated] .env file added with PORT and DB_CONN_LINK
├── nginx/                        # [Unchanged] Load balancer config
├── monitoring/                   # [New] Monitoring configurations
│   └── prometheus.yml              # Metrics collection rules
├── compose.monitoring.yml        # [New] Monitoring stack services
├── compose.yml                   # [Updated] Core application services
└── README.md
Enter fullscreen mode Exit fullscreen mode
# Launch complete monitoring stack
docker compose up
Enter fullscreen mode Exit fullscreen mode

Accessing Your Monitoring Stack

Once your stack is running, you can access the different components through these endpoints:

Prometheus Query Interface

Access Prometheus at http://localhost:9090/query to explore your metrics and run PromQL queries. The interface allows you to:

  • Execute queries to retrieve specific metrics
  • Visualize data trends over time
  • Monitor the health of your scrape targets

Prometheus Targets Monitoring

Navigate to the targets section at http://localhost:9090/targets to verify that Prometheus is successfully scraping metrics from Node Exporter and other configured endpoints. This view shows the status of each target and helps troubleshoot connectivity issues.

Grafana Dashboard Configuration

Initial Login

Access Grafana at http://localhost:3000 using the default credentials:

  • Username: admin
  • Password: admin

These credentials are configured in your Docker Compose file:

environment:
  - GF_SECURITY_ADMIN_USER=admin
  - GF_SECURITY_ADMIN_PASSWORD=admin
Enter fullscreen mode Exit fullscreen mode

Adding Prometheus as a Data Source

  1. Navigate to Configuration → Data Sources
  2. Click "Add data source"
  3. Select Prometheus
  4. Set the URL to http://prometheus:9090 (using Docker service name)
  5. Click "Save & Test" to verify the connection

Importing Pre-built Dashboards

Grafana's community provides excellent pre-built dashboards:

  1. Visit https://grafana.com/grafana/dashboards/
  2. Search for "Node Exporter Full" dashboard
  3. Copy the dashboard ID (1860)
  4. In Grafana, navigate to Dashboards → Import
  5. Paste the ID and click "Load"
  6. Select Prometheus as your data source
  7. Click "Import" to complete the setup

This dashboard provides comprehensive system monitoring including CPU, memory, disk, and network metrics with professional visualizations.

Key Takeaway

The combination of Prometheus, Node Exporter, and Grafana provides a solid foundation that scales from development environments to enterprise production systems. By implementing this stack early in your development process, you build reliability practices into your application lifecycle rather than retrofitting them later.

Future Enhancements

Advanced Monitoring

Implement container-specific monitoring using cAdvisor for granular insights into Docker container resource usage, complementing the current system-level monitoring approach.

Centralized Logging

Integrate OpenTelemetry or Promtail as log collectors, Loki for log aggregation, and enhance Grafana dashboards with log correlation capabilities for comprehensive observability.

Distributed Tracing

Add Jaeger or Zipkin integration to trace request flows through the microservice architecture, Tempo as storage and Grafana for visualization to enable detailed performance analysis and bottleneck identification across service boundaries.

Production Deployment

Implement GitHub Actions CI/CD pipeline for automated testing, building, and deployment to cloud infrastructure (AWS ECS, Google Cloud Run, or Azure Container Instances).

You can find the full project of this article at Github link

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.