What is Prometheus?
Prometheus is an open source monitoring system and time-series database. It scrapes metrics over HTTP from targets such as servers, containers, databases and applications, stores them efficiently on disk, and lets you query them with PromQL and trigger alerts. It is a graduated Cloud Native Computing Foundation project under the Apache-2.0 license and the default metrics backend for Grafana dashboards.
This guide runs Prometheus together with Node Exporter, the official exporter for Linux host metrics such as CPU, memory, disk and network.
Requirements
- A Linux server with Docker Engine and Docker Compose v2. A small setup needs about 1 vCPU and 1 GB of RAM; memory grows with the number of time series.
- Local disk for the database. A single host with Node Exporter produces modest data, but plan a few GB for months of retention.
Step 1: Prepare the server
This guide assumes Ubuntu 24.04 with Docker installed from the official Docker Engine guide.
mkdir -p ~/monitoring && cd ~/monitoring
Create prometheus.yml, the scrape configuration:
global:
scrape_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]
- job_name: node
static_configs:
- targets: ["host.docker.internal:9100"]
Step 2: Create the Docker Compose file
Node Exporter needs the host's network and process namespaces to report real host metrics, exactly as its official docs describe. Prometheus reaches it through host.docker.internal. Save this as docker-compose.yml:
services:
prometheus:
image: prom/prometheus:v3.15.0
container_name: prometheus
restart: unless-stopped
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=90d"
ports:
- "127.0.0.1:9090:9090"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
node-exporter:
image: prom/node-exporter:v1.12.1
container_name: node-exporter
restart: unless-stopped
command:
- "--path.rootfs=/host"
network_mode: host
pid: host
volumes:
- "/:/host:ro,rslave"
volumes:
prometheus_data:
Overriding command replaces the image defaults, so the config file and storage path are passed explicitly. The 90-day retention replaces the 15-day default.
Two notes on ports. Prometheus has no login, so 9090 is bound to 127.0.0.1; Docker-published ports bypass ufw, and "9090:9090" would put your metrics on the internet. Node Exporter uses host networking instead, so it listens on port 9100 like a normal host process, and here ufw does apply. Allow the Docker networks and keep it closed to everyone else:
sudo ufw allow from 172.16.0.0/12 to any port 9100 proto tcp
Step 3: Start and open the app
docker compose up -d
ssh -L 9090:127.0.0.1:9090 user@YOUR_SERVER_IP # run this on your laptop
Open http://localhost:9090, go to Status, Target health, and check that both prometheus and node are UP. Try a query such as node_memory_MemAvailable_bytes on the Query page.
To get dashboards, add Grafana to this same Compose file (see our Grafana guide), create a Prometheus data source with the URL http://prometheus:9090, and import dashboard ID 1860 (Node Exporter Full).
Step 4: Put it behind HTTPS
Most people never expose Prometheus and only publish Grafana. If you do need remote access, put it behind Caddy with authentication:
prometheus.example.com {
basic_auth {
admin PASTE_HASH_FROM_caddy_hash-password
}
reverse_proxy 127.0.0.1:9090
}
Generate the hash with caddy hash-password and a strong password such as the output of openssl rand -hex 32.
Backups and upgrades
Prometheus data is usually treated as replaceable, but if you want history to survive a disk failure, take a snapshot. That requires starting Prometheus with --web.enable-admin-api, then:
curl -XPOST http://127.0.0.1:9090/api/v1/admin/tsdb/snapshot
The snapshot appears under /prometheus/snapshots in the volume; copy it off-site. Always back up prometheus.yml and docker-compose.yml. To upgrade, bump the tags after reading the release notes, then:
docker compose pull && docker compose up -d
Troubleshooting
- Node target is DOWN with "connection refused" or a timeout: the firewall blocks 9100 from Docker networks, or the
extra_hostsline is missing. - "permission denied" on /prometheus: the image runs as user
nobody. Use a named volume, orchown 65534:65534a bind-mounted folder. - Config changes ignored: Prometheus reads the file at startup. Run
docker compose restart prometheus, or enable--web.enable-lifecycleand POST to/-/reload. - Disk keeps growing: lower the retention time or set
--storage.tsdb.retention.size.
Next steps
Add cAdvisor for per-container metrics, write alert rules and route them through Alertmanager, scrape exporters for your databases and apps, and add more servers by installing Node Exporter on each.
Spotted something out of date? Tell us and we will update the guide.