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"}
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
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:
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)