DEV Community

nicodemus
nicodemus

Posted on

Prometheus & Grafana: Complete Setup Guide

Monitoring Your App with Prometheus & Grafana

A simple, beginner-friendly walkthrough — from zero to a live dashboard.
What You're Building
Prometheus and Grafana work together to monitor your app:

Prometheus is the collector. It checks your app every few seconds and saves what it finds (requests, memory, errors).
Grafana is the visualizer. It turns Prometheus's raw numbers into graphs you can actually read.

Think of a fitness tracker: Prometheus is the device collecting your heart rate and steps. Grafana is the app that turns that into a nice chart.
By the end of this guide you'll have: your app exposing live stats, Prometheus collecting them, and a Grafana dashboard showing them as graphs.
Part 1 — Create the /metrics Endpoint in Your App
Prometheus can only collect data your app actually exposes. If you don't have a /metricsroute yet, here's how to add one using prometheus-client, the official Python library.
Install it:
pip install prometheus-client

If you're using FastAPI

pythonfrom fastapi import FastAPI
from prometheus_client import Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST
from starlette.responses import Response
import time

app = FastAPI()

# Define your metrics
REQUEST_COUNT = Counter(
    "api_requests_total", "Total API requests", ["method", "endpoint", "status"]
)
REQUEST_LATENCY = Histogram(
    "api_request_duration_seconds", "Request latency in seconds", ["endpoint"]
)

@app.middleware("http")
async def track_requests(request, call_next):
    start_time = time.time()
    response = await call_next(request)
    duration = time.time() - start_time

    REQUEST_COUNT.labels(
        method=request.method,
        endpoint=request.url.path,
        status=response.status_code,
    ).inc()
    REQUEST_LATENCY.labels(endpoint=request.url.path).observe(duration)

    return response

@app.get("/metrics")
def metrics():
    return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)

@app.get("/")
def root():
    return {"message": "Hello World"}
Enter fullscreen mode Exit fullscreen mode

Run your app
FastAPI:
uvicorn main:app --host 0.0.0.0 --port 8000
Confirm it works
curl http://localhost:8000/metrics

Part 2 — Expose Metrics From Your App
Before Prometheus can collect anything, your app needs a /metrics page. This is just a plain-text page listing your app's current stats.
Test if yours already has one:
curl http://localhost:8000/metrics
You should see something like this:


Got numbers back? Your app is ready. Move to Part 2.

Got "Connection refused"? Your app isn't running — start it first.

Got "404 Not Found"? Your app has no /metrics route yet — ask your developer to add one (most languages have a free metrics library for this).
[SCREENSHOT: Terminal output of curl http://localhost:8000/metrics]

Part 3 — Install Prometheus & Grafana
The easiest way to run both is with Docker. Create a file called _docker-compose.yml_ in a new folder:

yamlversion: '3'
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - monitoring

networks:
  monitoring:
    driver: bridge

Enter fullscreen mode Exit fullscreen mode

Start everything with one command:
docker-compose up -d
Check both containers are running:
docker ps
You should see prometheus and grafana listed as Up.
[SCREENSHOT: Terminal output of docker ps showing both containers running]

Part 4 — Configure Prometheus
Prometheus needs a config file telling it where your app lives. Create prometheus.yml in the same folder:

yamlglobal:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'my-app'
    static_configs:
      - targets: ['host.docker.internal:8000']
    metrics_path: '/metrics'
    scrape_interval: 5s
What this means, line by line:
Enter fullscreen mode Exit fullscreen mode

scrape_interval: 15s — how often Prometheus checks all apps by default
job_name — a label you choose for your app
targets — the address and port where your app runs
metrics_path — the page Prometheus reads from (almost always /metrics)

Replace host.docker.internal:8000 with your actual app's address and port. Restart Prometheus so it picks up the change:
docker restart prometheus

Part 5 — Verify Prometheus Is Collecting Data
Open Prometheus in your browser:
http://localhost:9090
[SCREENSHOT: Browser showing the Prometheus home page at localhost:9090]

Check your app shows as UP
Click Status → Targets. Your job name should show state UP.
[SCREENSHOT: Prometheus Targets page showing your app's status as UP]

If it shows DOWN: double-check the address in prometheus.ymlmatches where your app actually runs.
Run a test query
Click Graph, type a metric name, and click Execute:
api_requests_total
A line should appear on the graph. That confirms data is flowing.
[SCREENSHOT: Prometheus Graph tab showing a line for api_requests_total]

Part 6 — Connect Grafana to Prometheus

Open Grafana in your browser:
http://localhost:3000
Log in with username: adminand password: admin (or whatever you set in docker-compose.yml).
[SCREENSHOT: Grafana login page]

Now link it to Prometheus:

Go to Connections (or the gear icon) → Data Sources
Click Add data source → select Prometheus
Set the URL to http://prometheus:9090
Click Save & Test — you should see "Data source is working"

[SCREENSHOT: Grafana data source page showing 'Data source is working']

Part 7 — Build Your First Dashboard
Time to turn raw numbers into a graph you can actually look at.

Go to Dashboards → New Dashboard → Add panel
Set Data source to Prometheus
In the query box, type: api_requests_total
Click Run queries — a graph should appear above
Give the panel a title, e.g. "Total API Requests", then click Save

[SCREENSHOT: Grafana panel editor showing a live graph for Total API Requests Over Time]

Add two more panels the same way, using these queries:
mem_stats_heap_in_use
rate(go_gc_pauses_seconds_total[5m])
Name them "Memory Usage" and "GC Pause Rate". Save the dashboard with a name like "My App Monitoring".
[SCREENSHOT: Final Grafana dashboard with panels: Total API Requests Over Time, memory use,sum of heap allocation in bytes,processes in cpu,total_gc_cycles_total and sum go_gc pauses in second]

Reading Your Graphs
MetricWhat it tells youHealthy rangeAPI requests totalHow many requests your app has receivedSteady increaseMemory (heap in use)RAM your app is using right nowStable, ~300–400 MBGC pause timeTime spent pausing to clean up memoryUnder 50msCPU processesHow busy the server isRoughly steady, no constant spikes

Troubleshooting
ProblemLikely causeFixNo data in GrafanaPrometheus isn't collecting yetCheck Status → Targets is UP, wait 1–2 minConnection refusedYour API isn't runningStart the API, then re-test /metrics404 on /metricsNo metrics endpoint in your appAdd a metrics library/route to your APITarget shows DOWNWrong address in prometheus.ymlFix the target address, restart Prometheus

Next Steps

Share the dashboard link with your team
Check in on it daily for the first week to learn what "normal" looks like
Set up an alert for when memory usage climbs too high
Add more apps to the same Prometheus config as your system grows

Top comments (0)