Self-Hosted Monitoring: Prometheus & Grafana Setup

by Marcus Chen
Self-Hosted Monitoring: Prometheus & Grafana Setup

Server kamu down, kamu tahunya dari user

Itu skenario paling nyebelin yang pernah gue alami. User nge-DM duluan, bilang endpoint nggak bisa diakses, sementara gue lagi tidur siang. Nggak ada alert, nggak ada notifikasi, nggak ada apa-apa.

Masalahnya bukan karena gue males setup monitoring — tapi karena waktu itu gue pakai layanan monitoring SaaS yang punya free tier super terbatas. Begitu traffic naik dikit, dashboard-nya minta upgrade. Harga plan-nya? Lebih mahal dari VPS utama gue.

Solusinya: setup monitoring tools self-hosted dengan Prometheus dan Grafana. Gratis, data ada di server sendiri, dan bisa dikustomisasi sesuka hati. Artikel ini bakal nunjukin cara setup dari nol sampai alert jalan di VPS Ubuntu.


Kenapa Prometheus + Grafana, bukan yang lain?

Ada banyak pilihan monitoring stack. Tapi kombinasi Prometheus + Grafana ini yang paling banyak dipakai di production karena alasan yang masuk akal:

  • Prometheus adalah time-series database + scraper metrik. Dia yang narik data dari server dan aplikasi kamu.
  • Grafana adalah layer visualisasi. Dia yang bikin grafik cantik dari data Prometheus.
  • Keduanya open-source, komunitas besar, dokumentasi lengkap.
  • Bisa jalan di VPS dengan RAM 1GB sekalipun (asal nggak kebanyakan metrik).

Alternatif seperti Datadog atau New Relic memang bagus, tapi harganya bisa bikin dompet menangis kalau kamu indie hacker atau lagi belajar. This guide on managed WordPress hosting cost comparison menunjukkan bagaimana biaya infrastruktur bisa signifikan — self-hosted adalah pilihan yang lebih masuk akal untuk kontrol penuh.


Prerequisites sebelum mulai

Gue asumsikan kamu punya:

  • VPS Ubuntu 22.04 (minimal 1 vCPU, 1GB RAM)
  • Akses root atau user dengan sudo
  • Docker dan Docker Compose sudah terinstall
  • Domain atau IP publik yang bisa diakses

Kalau Docker belum ada, install dulu:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker

Setup stack dengan Docker Compose

Gue prefer pakai Docker Compose supaya semua service terdefinisi jelas dan gampang di-restart atau di-update.

Buat direktori project:

mkdir ~/monitoring && cd ~/monitoring

Buat file docker-compose.yml:

version: '3.8'

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus_data:
  grafana_data:

services:
  prometheus:
    image: prom/prometheus:v2.51.0
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=15d'
      - '--web.enable-lifecycle'
    ports:
      - '9090:9090'
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter:v1.7.0
    container_name: node-exporter
    restart: unless-stopped
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    ports:
      - '9100:9100'
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:10.4.0
    container_name: grafana
    restart: unless-stopped
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=gantipasswordini
      - GF_USERS_ALLOW_SIGN_UP=false
    ports:
      - '3000:3000'
    networks:
      - monitoring
    depends_on:
      - prometheus

Sekarang buat file konfigurasi Prometheus, prometheus.yml:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

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

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

Jalankan stack-nya:

docker compose up -d

Cek semua container jalan:

docker compose ps

Output yang kamu harapkan:

NAME             STATUS
grafana          running
node-exporter    running
prometheus       running

Akses Grafana dan connect ke Prometheus

Buka browser, akses http://IP_VPS_KAMU:3000. Login dengan:

  • Username: admin
  • Password: gantipasswordini (atau yang kamu set di env)

Langkah selanjutnya:

  1. Klik Connections → Data sources → Add data source
  2. Pilih Prometheus
  3. Di field URL, isi: http://prometheus:9090
  4. Klik Save & test — kalau muncul "Successfully queried the Prometheus API", berarti sudah konek.

Sekarang import dashboard yang sudah jadi supaya nggak perlu bikin dari nol. Dashboard Node Exporter Full punya ID 1860 di Grafana.com — ini yang paling populer.

  1. Klik Dashboards → Import
  2. Isi ID 1860, klik Load
  3. Pilih Prometheus data source yang tadi
  4. Klik Import

Dalam hitungan detik kamu sudah punya dashboard dengan CPU usage, memory, disk I/O, dan network traffic dari server kamu.


Setup alerting supaya nggak kecolongan lagi

Dashboard cantik nggak berguna kalau kamu nggak tahu kalau server lagi bermasalah. Setup alert di Grafana supaya dapat notifikasi ke Telegram atau email.

Alert via Telegram

Pertama, buat Telegram bot dulu via @BotFather dan catat token-nya. Lalu dapatkan chat ID kamu dengan kirim pesan ke bot, terus akses:

https://api.telegram.org/bot<TOKEN>/getUpdates

Cari field chat.id di response JSON-nya.

Di Grafana:

  1. Klik Alerting → Contact points → Add contact point
  2. Pilih tipe Telegram
  3. Isi Bot API Token dan Chat ID
  4. Klik Test untuk verifikasi
  5. Simpan

Sekarang buat alert rule untuk CPU usage tinggi:

  1. Buka dashboard Node Exporter
  2. Klik panel CPU Usage → Edit
  3. Masuk ke tab Alert
  4. Klik Create alert rule from this panel
  5. Set kondisi: avg() of A > 80 (alert kalau CPU di atas 80% selama 5 menit)
  6. Di bagian Notifications, pilih contact point Telegram yang tadi
  7. Simpan

Dari sekarang, kalau CPU server kamu spike di atas 80% lebih dari 5 menit, Telegram kamu langsung bunyi.


Gotcha yang gue temuin waktu setup

Beberapa hal yang bikin gue buang waktu dan semoga kamu bisa skip:

1. Port 9090 dan 3000 jangan expose ke publik tanpa auth

Default setup ini expose semua port langsung ke internet. Siapapun bisa akses Prometheus UI kamu di port 9090. Solusinya: pakai Nginx sebagai reverse proxy dengan basic auth, atau restrict akses via firewall:

# Izinkan hanya IP tertentu akses port 9090
sudo ufw allow from 1.2.3.4 to any port 9090
sudo ufw deny 9090

2. node-exporter nggak bisa akses /proc host kalau pakai Docker Desktop di Mac

Ini cuma masalah kalau kamu develop di Mac. Di production Linux VPS, volume mount /proc:/host/proc:ro jalan normal. Di Mac, skip dulu dan test langsung di VPS.

3. Retention data Prometheus default cuma 15 hari

Gue set --storage.tsdb.retention.time=15d di compose file. Kalau mau lebih lama, naikkan — tapi perhatikan disk space. Prometheus bisa makan banyak storage kalau kamu punya banyak metrik dan retention panjang. Cek usage:

docker exec prometheus du -sh /prometheus

4. Grafana admin password di env variable nggak update kalau container sudah pernah jalan

Kalau kamu ganti GF_SECURITY_ADMIN_PASSWORD di compose file setelah container pertama kali jalan, password lama tetap berlaku karena sudah tersimpan di volume. Reset manual lewat CLI:

docker exec -it grafana grafana-cli admin reset-admin-password passwordbaru

Monitor aplikasi kamu sendiri

Node Exporter hanya monitor metrik sistem (CPU, RAM, disk). Kalau kamu punya aplikasi — misalnya API Node.js atau Python Flask — kamu bisa expose metrik custom ke Prometheus.

Contoh untuk Node.js dengan library prom-client:

const client = require('prom-client');
const express = require('express');
const app = express();

// Collect default metrics (event loop lag, memory, dll)
client.collectDefaultMetrics();

// Custom counter untuk request HTTP
const httpRequests = new client.Counter({
  name: 'http_requests_total',
  help: 'Total HTTP requests',
  labelNames: ['method', 'route', 'status'],
});

// Middleware untuk tracking
app.use((req, res, next) => {
  res.on('finish', () => {
    httpRequests.inc({
      method: req.method,
      route: req.route?.path || req.path,
      status: res.statusCode,
    });
  });
  next();
});

// Endpoint yang di-scrape Prometheus
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', client.register.contentType);
  res.end(await client.register.metrics());
});

app.listen(3001, () => console.log('App running on port 3001'));

Tambahkan job baru di prometheus.yml:

  - job_name: 'nodejs-app'
    static_configs:
      - targets: ['HOST_IP:3001']

Reload Prometheus tanpa restart:

curl -X POST http://localhost:9090/-/reload

Sekarang metrik aplikasi kamu masuk ke Prometheus dan bisa divisualisasikan di Grafana.


Langkah lanjutan yang gue lakuin

Setup di atas sudah cukup untuk monitoring dasar yang solid. Yang gue lakuin setelah ini:

  1. Pasang Nginx reverse proxy dengan SSL (Let's Encrypt) di depan Grafana supaya bisa akses lewat domain dengan HTTPS — bukan lewat port 3000 langsung.
  2. Tambah Alertmanager untuk routing alert yang lebih kompleks — misalnya alert CPU ke satu channel Telegram, alert disk ke channel lain.
  3. Setup Loki untuk log aggregation supaya bisa search log dari Grafana juga, bukan cuma metrik.
  4. Export dashboard sebagai JSON dan simpan di Git supaya kalau VPS-nya mati dan harus rebuild, konfigurasi dashboard nggak hilang.

Kalau kamu mau setup cara mempercepat loading WordPress atau aplikasi lain dengan monitoring yang solid, explore fitur federation di Prometheus — satu Prometheus pusat bisa scrape dari Prometheus lain di server yang berbeda.

Total biaya setup ini? Nol rupiah tambahan di luar VPS yang sudah kamu bayar. Data metrik ada di server kamu sendiri, nggak ada vendor lock-in, dan kamu tahu persis apa yang terjadi di infrastruktur kamu.