# How to self-host Prometheus with Docker Compose

> Prometheus Docker Compose setup with Node Exporter for host metrics: prometheus.yml, retention, localhost-only ports, Grafana dashboards, backups and upgrades.

## Key facts

| Fact | Value |
|---|---|
| App | Prometheus (https://appsgit.com/apps/prometheus) |
| Difficulty | intermediate |
| Time | about 20 minutes |
| Requirements | 1 vCPU / 1 GB RAM (more with many targets); Docker + Docker Compose v2; Local disk for the time-series database; Grafana (optional, for dashboards) |
| Last updated | 2026-10-06 |

## 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](https://docs.docker.com/engine/install/ubuntu/).

```bash
mkdir -p ~/monitoring && cd ~/monitoring
```

Create `prometheus.yml`, the scrape configuration:

```yaml
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`:

```yaml
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:

```bash
sudo ufw allow from 172.16.0.0/12 to any port 9100 proto tcp
```

## Step 3: Start and open the app

```bash
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](https://appsgit.com/guides/grafana)), 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](https://caddyserver.com/docs/) with authentication:

```caddyfile
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:

```bash
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:

```bash
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_hosts` line is missing.
- **"permission denied" on /prometheus:** the image runs as user `nobody`. Use a named volume, or `chown 65534:65534` a bind-mounted folder.
- **Config changes ignored:** Prometheus reads the file at startup. Run `docker compose restart prometheus`, or enable `--web.enable-lifecycle` and 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.

## FAQ

### What port does Prometheus use?

The Prometheus server and web UI listen on port 9090. Node Exporter serves host metrics on port 9100, and other exporters use their own ports.

### Does Prometheus have a login or password?

No. Prometheus has no authentication by default, which is why this guide binds port 9090 to localhost. Add basic auth through a web config file or put it behind a reverse proxy with authentication before exposing it.

### Is Prometheus free?

Yes. Prometheus is a free, open source Cloud Native Computing Foundation project under the Apache-2.0 license, with no paid edition.

### Prometheus vs Grafana: what is the difference?

Prometheus collects and stores metrics and evaluates alert rules. Grafana draws dashboards from data sources, Prometheus being the most common. Most setups run both: Prometheus as the database, Grafana as the interface.

### How long does Prometheus keep data?

Prometheus keeps 15 days of data by default. Change it with the --storage.tsdb.retention.time flag (for example 90d), or cap disk use with --storage.tsdb.retention.size.

### Prometheus vs InfluxDB?

Prometheus pulls metrics from exporters on a schedule and has its own query language, PromQL, built for monitoring and alerting. InfluxDB is a general time-series database that applications push data into. For server and container monitoring, Prometheus has the larger exporter ecosystem.

Prefer not to do it yourself? [appsgit installation help](https://appsgit.com/services/install) installs it on your server for a fixed quote.

---

Canonical page: https://appsgit.com/guides/prometheus
Source: appsgit (https://appsgit.com), the app store for github. Data from the GitHub API, refreshed nightly.
Machine access: JSON API https://appsgit.com/api/v1/apps (OpenAPI: https://appsgit.com/openapi.json), MCP server https://mcp.appsgit.com/mcp, full index https://appsgit.com/llms-full.txt.
