Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Prometheus and Grafana: Full Monitoring Stack

By Kokil Thapa | Last reviewed: August 2026

Setting up a reliable Prometheus and Grafana: Full Monitoring Stack is the difference between guessing why your Laravel application slowed down during peak hours and knowing exactly which database query caused the bottleneck. For developers managing production infrastructure in Nepal or remotely, relying solely on basic uptime checks or shared hosting dashboards is insufficient for debugging complex performance issues. This guide walks through a battle-tested deployment on Ubuntu 24.04 LTS, focusing on practical security, resource management, and application-level integration rather than theoretical architecture.

How do you architect the Prometheus and Grafana full monitoring stack?

Understanding the data flow prevents misconfiguration later. In my experience working on production Laravel applications, treating monitoring as an afterthought leads to gaps where critical metrics are missing during incidents. The DevOps automation principles apply directly here: every component must be declarative and reproducible.

Prometheus ServerTSDB + Alert RulesPort 9090Node Exporter:9100Laravel App/metrics :8000GrafanaDashboards + AlertsPort 3000ScrapeScrapeQuery APIPersistent Storage (/var/lib/prometheus)Time-Series Database Blocks
Prometheus and Grafana full monitoring stack architecture showing pull-based scraping model and data persistence layer

Prometheus operates on a pull model, actively scraping HTTP endpoints at defined intervals. This contrasts with push-based systems and has significant implications for firewall rules and service discovery. Grafana connects to Prometheus purely as a read-only data source; it never writes metrics back. On a typical Laravel project, I run Node Exporter for OS-level metrics (CPU, memory, disk I/O) alongside a PHP-FPM exporter and an application-specific endpoint exposing business metrics like queue depth or payment processing latency.

Component responsibilities

  • Prometheus: Scrapes targets every 15–60 seconds, evaluates alerting rules, stores compressed time-series blocks on disk.
  • Grafana: Queries PromQL against the Prometheus HTTP API, renders visualizations, manages user authentication and dashboard permissions.
  • Node Exporter: Exposes hardware and OS metrics from /proc and /sys without requiring root privileges when configured correctly.
  • Application Exporters: Custom or community-maintained adapters that translate application state into Prometheus exposition format.

How do you install and configure Prometheus on Ubuntu 24.04?

Avoid installing Prometheus via apt unless you understand the version lag. The official repositories often carry outdated releases missing recent security patches or PromQL functions. Download the latest stable binary (2.53.x as of mid-2026) directly from GitHub releases. This approach also simplifies upgrades and rollback.

# Create system user and directories
sudo useradd --no-create-home --shell /bin/false prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus

# Download and extract (verify checksum in production)
wget https://github.com/prometheus/prometheus/releases/download/v2.53.1/prometheus-2.53.1.linux-amd64.tar.gz
tar xvfz prometheus-2.53.1.linux-amd64.tar.gz
sudo cp prometheus-2.53.1.linux-amd64/prometheus /usr/local/bin/
sudo cp prometheus-2.53.1.linux-amd64/promtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool

The configuration file determines what gets monitored. A common mistake in tutorials is omitting scrape timeouts or setting unrealistic intervals. For most web applications, a 30-second interval balances freshness with resource consumption. Always validate configuration before reloading:

# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 30s
  evaluation_interval: 30s
  scrape_timeout: 10s

rule_files:
  - "alerts/*.yml"

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

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

  - job_name: 'laravel-app'
    metrics_path: '/metrics'
    scheme: https
    authorization:
      type: Bearer
      credentials_file: /etc/prometheus/bearer_token
    static_configs:
      - targets: ['your-domain.com']

Create the systemd unit file to manage the service. Note the explicit storage path and retention flags — these prevent disk exhaustion on smaller VPS instances commonly used in Nepal:

# /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus Monitoring System
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 \
  --storage.tsdb.retention.time=30d \
  --storage.tsdb.retention.size=8GB \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.enable-lifecycle

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Validating and starting the service

  1. Run sudo -u prometheus promtool check config /etc/prometheus/prometheus.yml to catch syntax errors.
  2. Execute sudo systemctl daemon-reload && sudo systemctl enable --now prometheus.
  3. Verify status with systemctl status prometheus and check journalctl -u prometheus -f for startup warnings.
  4. Access http://localhost:9090/targets to confirm all scrape targets show UP status.

How do you secure Prometheus and Grafana endpoints in production?

This is where most tutorials fail catastrophically. Exposing unauthenticated Prometheus or Grafana to the public internet leaks sensitive infrastructure data and creates attack vectors. In my experience maintaining legal-tech portals handling sensitive documents, security cannot be optional. Reference the server security hardening guide for foundational UFW and fail2ban setup before proceeding.

Public InternetUntrustedNginx Reverse ProxyBasic Auth / IP WhitelistTLS Termination/prometheus → localhost:9090/grafana → localhost:3000Prometheus127.0.0.1:9090NO Public BindGrafana127.0.0.1:3000NO Public BindHTTPS OnlyLocalhostLocalhost
Production security topology for Prometheus and Grafana full monitoring stack with Nginx reverse proxy and localhost-only binding

Bind services to localhost only

Never let Prometheus or Grafana listen on 0.0.0.0. Modify the systemd unit for Prometheus to include --web.listen-address=127.0.0.1:9090. For Grafana, edit /etc/grafana/grafana.ini:

[server]
http_addr = 127.0.0.1
http_port = 3000
domain = your-domain.com
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
serve_from_sub_path = true

Nginx reverse proxy with authentication

Use HTTP Basic Auth as a minimum barrier. Generate credentials with htpasswd -c /etc/nginx/.htpasswd monitor_user. The following Nginx configuration assumes Let's Encrypt certificates are already provisioned:

server {
    listen 443 ssl http2;
    server_name monitor.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/monitor.your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/monitor.your-domain.com/privkey.pem;

    location /prometheus/ {
        auth_basic "Monitoring Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:9090/;
        proxy_set_header Host $host;
    }

    location /grafana/ {
        auth_basic "Monitoring Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:3000/;
        proxy_set_header Host $host;
    }
}

For higher-security environments, replace Basic Auth with OAuth2-proxy or Cloudflare Access. IP whitelisting via UFW provides defense-in-depth but should not be the sole protection mechanism since IP addresses can be spoofed or change unexpectedly.

How do you integrate Laravel application metrics with Prometheus?

Infrastructure metrics alone don't tell you if your business logic is healthy. For Laravel applications, expose custom metrics using the spatie/laravel-prometheus package or the lower-level promphp/prometheus_client_php library. This bridges the gap between server health and application behavior, which is critical when debugging issues reported by clients using platforms like legal case tracking systems where downtime directly impacts court deadlines.

// Install via Composer
composer require spatie/laravel-prometheus

// Publish config
php artisan vendor:publish --tag=prometheus-config

// In app/Providers/AppServiceProvider.php boot() method
use Spatie\Prometheus\Collectors\Laravel\QueueCollector;
use Spatie\Prometheus\Collectors\Laravel\CacheCollector;

public function boot(): void
{
    Prometheus::registerCollector(new QueueCollector());
    Prometheus::registerCollector(new CacheCollector());
    
    // Custom business metric example
    Prometheus::registerCollector(
        new class implements Collector {
            public function collect(): array
            {
                return [
                    Metric::gauge('pending_attestation_requests')
                        ->help('Number of attestation requests awaiting review')
                        ->value(fn() => AttestationRequest::whereStatus('pending')->count()),
                ];
            }
        }
    );
}

Essential Laravel metrics to track

Metric NameTypeWhy It Matters
laravel_queue_jobs_totalCounterDetect queue backup before users notice delayed emails or notifications
laravel_cache_hit_ratioGaugeLow ratio indicates cache invalidation problems or missing cache keys
laravel_http_request_duration_secondsHistogramP95/P99 latency reveals slow endpoints invisible to average-response-time monitoring
pending_attestation_requestsGaugeBusiness-specific SLA compliance for legal-tech workflows
php_fpm_active_processesGaugeApproaching max_children causes request queuing and 502 errors

Protect the /metrics endpoint with bearer token authentication or IP restriction. Never expose it publicly. Add middleware to validate the token matches the credential file referenced in your Prometheus scrape configuration.

How do you configure alerting rules and Grafana dashboards effectively?

Dashboards without alerts create passive monitoring that fails during incidents. Define alerting rules in Prometheus itself, not just in Grafana, to ensure alerts fire even if Grafana is down. Store rules in separate YAML files under /etc/prometheus/alerts/:

# /etc/prometheus/alerts/laravel.yml
groups:
  - name: laravel-application
    rules:
      - alert: HighQueueBacklog
        expr: laravel_queue_jobs_pending > 100
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Queue backlog exceeding threshold"
          description: "{{ $labels.job }} has {{ $value }} pending jobs for 5 minutes"

      - alert: LowCacheHitRate
        expr: rate(laravel_cache_hits_total[5m]) / rate(laravel_cache_operations_total[5m])  0.7
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Cache hit rate below 70%"
          description: "Application may be experiencing excessive database load"
Metric Valuequeue_jobs > 100Threshold CrossedPending Statefor: 5m TimerCondition Must PersistFiring StateAlert ActiveNotification SentReceiverSlack / EmailPagerDutyCommon Mistakes to Avoid✗ Missing 'for' clause causes alert flapping on transient spikes✗ No annotations leaves responders without context or runbook links✗ Alerting on symptoms instead of causes generates noise fatigue
Alert lifecycle in Prometheus and Grafana full monitoring stack with common configuration pitfalls highlighted

Building useful Grafana dashboards

Import community dashboards as starting points, then customize ruthlessly. Dashboard ID 1860 (Node Exporter Full) and 11277 (Laravel Stats) cover 80% of needs. However, always add a top-level row with business KPIs relevant to your specific application. For eCommerce sites, this means order conversion rates and payment gateway error counts. For legal portals, document submission volumes and average processing times matter more than generic CPU graphs.

Configure Grafana provisioning to make dashboards and data sources version-controlled. Place YAML files in /etc/grafana/provisioning/dashboards/ and /etc/grafana/provisioning/datasources/. This ensures your monitoring stack survives server rebuilds and can be replicated across staging and production environments using the same CI/CD pipeline patterns used for application code.

What retention and performance tuning prevents disk exhaustion?

Prometheus stores raw samples for the configured retention period, then compacts them into larger blocks. Without explicit limits, a busy server can consume all available disk space within weeks. Set both time-based and size-based retention in the systemd unit as shown earlier. The size limit acts as a safety valve when unexpected metric cardinality explosions occur.

Monitor Prometheus's own internal metrics to detect problems early. Key indicators include prometheus_tsdb_head_chunks (memory pressure), prometheus_tsdb_compactions_failed_total (disk I/O issues), and prometheus_target_scrape_pool_sync_total (configuration reload problems). If head chunks consistently exceed 1 million on a 4GB RAM server, reduce scrape frequency or drop high-cardinality labels.

For long-term retention beyond 30 days, consider Thanos or Mimir as object-storage-backed solutions. However, for most small-to-medium deployments in Nepal, rotating 30-day local retention with weekly offsite backups of the /var/lib/prometheus directory provides adequate history without operational complexity. Test restore procedures quarterly — backups you cannot restore are worthless.

Deploying Your Monitoring Stack With Confidence

The Prometheus and Grafana: Full Monitoring Stack gives you ownership over observability without vendor lock-in or recurring SaaS fees. Start with the core three components (Prometheus, Grafana, Node Exporter), secure them properly behind Nginx, and add application metrics incrementally based on actual incident post-mortems. Resist the urge to instrument everything upfront; monitor what hurts first. If you need assistance implementing this stack for your Laravel application or configuring production-grade alerting for Nepal-based infrastructure, reach out to discuss your monitoring requirements.

Frequently Asked Questions

A combined open-source observability solution where Prometheus scrapes and stores time-series metrics while Grafana visualizes them. This pairing provides real-time alerting, historical trending, and customizable dashboards for infrastructure and application performance without licensing fees.

The software is free and open-source. Costs are purely infrastructure. A basic production setup on a Nepal VPS runs Rs 1,500–3,000 monthly (~USD 11–22). Budget increases with metric cardinality and retention needs, as Prometheus storage scales linearly with data volume.

Choose Prometheus when you need full data ownership, have Linux administration skills, and want to avoid per-host SaaS fees exceeding Rs 10,000 monthly. SaaS tools suit teams lacking DevOps capacity, but self-hosting eliminates vendor lock-in and unpredictable billing spikes common in managed observability platforms.

Start with 4GB RAM and 2 vCPUs for small fleets under 50 targets. Prometheus consumes memory proportional to active series count, not target count. In my experience deploying this on Ubuntu 24 servers, allocate 50GB SSD minimum for two weeks retention at moderate cardinality. Monitor disk usage closely during initial tuning.

Set global scrape_interval to 15s for most applications. Use 30s or 60s for non-critical batch jobs to reduce cardinality. Never go below 10s unless profiling high-frequency trading systems. Shorter intervals increase storage costs linearly. Override per job in prometheus.yml rather than changing globals, allowing granular control across diverse service types.

High cardinality from unbounded labels like user_id, request_id, or timestamps causes memory exhaustion. Each unique label combination creates a new time series. Audit metrics using /api/v1/status/tsdb and enforce label sanitization at the exporter level. Drop unnecessary labels via relabel_configs before ingestion. This is the most common production issue I encounter during stack deployments.

Never expose Prometheus directly to the internet; it lacks authentication. Place both behind Nginx reverse proxy with HTTP basic auth or OAuth2. Enable TLS everywhere. Restrict Grafana admin access and use viewer-only roles for dashboards. Configure UFW rules allowing only trusted internal networks to reach metric endpoints. Treat monitoring infrastructure with same security rigor as application servers.

Yes, using packages like spatie/laravel-prometheus or arkaitzgarro/laravel-prometheus-exporter. Expose business metrics like queue depth, failed jobs, payment gateway latency, and Eloquent query counts. Combine with node_exporter for system metrics. On legal-tech portals I have built, this revealed slow document generation bottlenecks that application logs alone missed. Custom histograms provide percentile insights crucial for SLA compliance.

Alert only on symptoms users experience, not causes. Define severity levels: critical pages immediately, warning triggers investigation during business hours. Use inhibition rules to suppress downstream alerts when upstream services fail. Implement alert grouping by service and namespace. Test every rule against historical data before enabling. Untested alerts generate noise that erodes team trust in the monitoring system.

Default 15 days suits most operational debugging. Extend to 30-90 days for capacity planning and trend analysis. Longer retention requires proportionally more disk space. For yearly trends, enable remote_write to long-term storage like Thanos or VictoriaMetrics instead of expanding local retention. Balance compliance requirements against storage costs, especially on budget-constrained Nepal hosting environments.

Add Prometheus as a data source in Grafana configuration using the internal HTTP endpoint URL. No API keys needed for same-network setups. Enable browser access mode only if Grafana and Prometheus share network context. Configure query timeout matching your scrape interval multiplied by evaluation cycles. Test connection validates reachability. Multiple Prometheus instances can feed single Grafana for federated multi-environment views.

Essential exporters include node_exporter for OS metrics, mysqld_exporter or postgres_exporter for databases, redis_exporter for caching, and nginx_exporter for web servers. Application-specific exporters vary by stack. Avoid installing exporters speculatively; each adds cardinality. On WooCommerce sites I maintain, combining php-fpm exporter with custom WordPress metrics catches plugin performance regressions before customers report checkout failures.

Always validate config with promtool check config prometheus.yml before applying. Send SIGHUP signal or POST to /-/reload endpoint for zero-downtime updates. Never restart the service for config changes. Maintain configuration in Git with CI validation pipeline. Broken configs silently fail scraping until fixed. On shared EC2 deployments managing multiple sister sites, automated validation prevents one misconfigured job from breaking entire monitoring fleet.

Yes, Prometheus supports federated scraping across unlimited targets. Group servers by environment, role, or geography using separate job definitions. Use file_sd_configs or service discovery for dynamic target management instead of hardcoded lists. Single instance handles hundreds of nodes comfortably if cardinality stays controlled. For geographically distributed Nepal and international infrastructure, consider regional Prometheus instances with federation to central Grafana.

Skipping resource limits leads to OOM kills during traffic spikes. Ignoring label cardinality causes gradual memory creep. Missing backup strategy loses historical data during disk failures. Deploying without authentication exposes sensitive metrics publicly. Neglecting alert testing creates false confidence. Starting too complex delays value delivery. Begin with core system metrics, validate end-to-end flow, then incrementally add application instrumentation based on actual operational pain points observed in production.

Share this article

Quick Contact Options
Choose how you want to connect me: