A reliable Prometheus Compose setup uses two files: a compose.yml that runs Prometheus and its exporters, and a prometheus.yml that defines scrape jobs. Mount the configuration at /etc/prometheus/prometheus.yml, persist the time-series database at /prometheus, and address other containers by their Compose service names.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What this Docker Compose stack does
Docker Compose is a tool for defining and running multi-container applications from one YAML file. In a monitoring project, Compose gives Prometheus a repeatable way to start, stop, inspect, rebuild, and log the containers that provide metrics.
As an Amazon Associate I earn from qualifying purchases.
Prometheus collects metrics by scraping HTTP endpoints exposed by monitored targets. A basic project contains:
- Prometheus: stores time-series data and provides the web interface on port 9090.
- Exporters or instrumented applications: expose metrics for Prometheus to scrape.
- Persistent storage: keeps the Prometheus data directory across container recreation and restarts.
The following examples use the official Prometheus image and a standard cAdvisor image. Image tags are deliberately unpinned in the examples; for production, choose and test a specific tag rather than accepting an unexpected future change.
#1 Best Overall
Minimal Prometheus-only Compose file
Create a directory such as prometheus-compose. Put this file in it as compose.yml:
services:
prometheus:
image: prom/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
ports:
- '127.0.0.1:9090:9090'
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
volumes:
prometheus-data:
The loopback address in the port mapping makes the web interface reachable only from the Docker host. If the service must be reachable from another machine, use a firewall and an authenticated reverse proxy rather than exposing an unauthenticated Prometheus interface broadly.
Free tools Windows power users keep installed
One-click scans. No signup required.
Why each line matters
--config.filetells Prometheus exactly where the mounted configuration lives. The default path is commonly the same, but stating it removes ambiguity.--storage.tsdb.path=/prometheusputs the time-series database in the directory backed by the named volume../prometheus.yml:/etc/prometheus/prometheus.yml:robind-mounts the host file read-only. Prometheus can read it, but a process in the container cannot modify the host copy.prometheus-data:/prometheusgives the database durable storage. Removing the container does not remove this named volume; removing the volume does.127.0.0.1:9090:9090maps host port 9090 to the container’s port 9090 without publishing it on every host interface.
Write the Prometheus configuration
Create prometheus.yml beside compose.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['prometheus:9090']
The global block supplies defaults. scrape_interval: 15s means Prometheus normally requests metrics from each target every 15 seconds. evaluation_interval: 15s controls how often recording and alerting rules are evaluated when rules are configured.
Each entry under scrape_configs is a job. The static_configs list contains target addresses. Inside a Compose network, prometheus resolves to the Prometheus service, so prometheus:9090 is the correct target. Do not use localhost:9090 for another container: inside the Prometheus container, localhost means Prometheus itself.
Loading recording and alerting rules
Rules are separate YAML files. Add a rule_files entry when you have them:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['prometheus:9090']
Mount a host rules directory read-only if you use this layout:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./rules:/etc/prometheus/rules:ro
- prometheus-data:/prometheus
A configuration file can be syntactically valid while a target is unreachable. Validation and the Targets page are both required checks.
Start and verify the stack
- Create the files. Ensure
compose.ymlandprometheus.ymlare in the same directory, with indentation made from spaces rather than tabs. - Render the Compose model. Run
docker compose config. This catches YAML and Compose interpolation errors before containers start. - Start in the background. Run
docker compose up -d. The older commanddocker-compose upbelongs to the legacy Compose executable; current installations generally use the space-separated command. - Check container state. Run
docker compose ps. The Prometheus container should show a running state and a port mapping for 9090. - Read startup logs. Run
docker compose logs prometheus. Add-fto follow the stream while diagnosing startup or scrape problems. - Open the interface. Visit
http://localhost:9090on the Docker host. The expression browser can query stored series. - Inspect targets. Open the targets page in the Prometheus interface and confirm the
prometheusjob is UP. A target marked down needs network or endpoint diagnosis even when the container itself is running. - Check the endpoint directly. Visit
http://localhost:9090/metricsto confirm Prometheus is exposing its own metrics endpoint.
When a service is changed, apply the new definition with docker compose up -d. Use docker compose restart prometheus only when a restart is actually needed; a configuration reload can avoid interrupting the process.
Full example with cAdvisor and Redis
The official cAdvisor Compose pattern adds cadvisor and redis services. cAdvisor exposes container and host-level container metrics on port 8080. Redis is an application service in this template; merely publishing port 6379 does not make Redis metrics available to Prometheus. To monitor Redis-specific statistics, deploy a Redis exporter and add that exporter as a separate scrape target.
For a Linux Docker host, this is a practical starting point:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
services:
prometheus:
image: prom/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
ports:
- '127.0.0.1:9090:9090'
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
depends_on:
- cadvisor
cadvisor:
image: gcr.io/cadvisor/cadvisor
ports:
- '127.0.0.1:8080:8080'
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
redis:
image: redis
ports:
- '127.0.0.1:6379:6379'
volumes:
prometheus-data:
The host mounts give cAdvisor visibility into the filesystems, runtime state, kernel information, and Docker storage it needs to observe containers. They are intentionally read-only. The exact mounts can differ with the container runtime, operating system, rootless Docker, and security policy; if cAdvisor starts but reports permission or missing-statistics errors, inspect its logs and adapt the mounts to that host.
depends_on expresses startup order. It does not prove that cAdvisor is ready to serve metrics, nor does it restart a failed dependency. Prometheus will retry scrapes according to its normal behavior, so readiness still has to be checked on the Targets page.
Scrape cAdvisor by service name
Replace the minimal configuration with this one to collect both Prometheus and cAdvisor data:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['prometheus:9090']
- job_name: cadvisor
scrape_interval: 5s
static_configs:
- targets: ['cadvisor:8080']
The cAdvisor job uses a five-second interval, while the global default remains 15 seconds for jobs that do not override it. Faster scraping increases collection frequency and storage activity, so use it only where the extra resolution is useful.
Recommended Free Tools
From the Docker host, cAdvisor is available at http://localhost:8080 with the example port mapping. From Prometheus, the correct address is cadvisor:8080. The two addresses are not interchangeable: one is for a host browser, the other is for container-to-container DNS.
Persisting Prometheus data correctly
Prometheus stores its time-series database under /prometheus in the official container guidance. Without a named volume or bind mount at that path, data in the container’s writable layer can disappear when the container is replaced or its storage is removed.
Named volume
The examples use a named volume:
volumes:
- prometheus-data:/prometheus
This is the least intrusive option for a single host. Docker manages the volume location, and it remains available when you run docker compose down without the -v option. Running docker compose down -v removes declared volumes and therefore destroys the stored series.
Bind mount
A bind mount lets you choose the host directory and use host-level backup tooling:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #4
volumes:
- ./data/prometheus:/prometheus
Create the directory first and verify that the container’s Prometheus process can write to it. Bind mounts make permissions, ownership, disk capacity, and host cleanup policies your responsibility.
Operational checks
- Monitor free space on the filesystem holding the volume.
- Do not treat a container image update as a backup; back up the data volume independently.
- Keep the configuration and rules under version control, but do not assume that configuration files contain the time-series database.
- Before deleting a project, distinguish
docker compose downfromdocker compose down -v.
Adding Grafana to the same project
Grafana is a visualization layer, not a replacement for Prometheus. Add it when you need dashboards and user-facing charts. A simple service can share the Compose network:
grafana:
image: grafana/grafana
ports:
- '127.0.0.1:3000:3000'
depends_on:
- prometheus
volumes:
- grafana-data:/var/lib/grafana
Add the corresponding top-level volume:
volumes:
prometheus-data:
grafana-data:
When configuring the Prometheus data source in Grafana, use http://prometheus:9090. Using http://localhost:9090 from inside the Grafana container points back to Grafana itself, not to the Prometheus service. Keep Grafana’s port bound to loopback or protect it with your chosen authentication and reverse-proxy controls before exposing it outside the host.
Reloading configuration without replacing the container
Prometheus can reload a valid configuration after receiving SIGHUP or a POST request to /-/reload. The POST endpoint is available only when lifecycle support is enabled.
Add the lifecycle flag to the Prometheus command:
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --web.enable-lifecycle
After editing the bind-mounted file, validate the Compose model and then request a reload:
docker compose exec prometheus wget -qO- --post-data='' http://localhost:9090/-/reload
You can also send SIGHUP to the container’s main process with Docker tooling. Restrict access to the lifecycle endpoint; do not publish an unauthenticated administration surface to an untrusted network.
Best Value
If the new YAML is malformed or otherwise invalid, Prometheus keeps the last valid configuration rather than applying the broken one. Check docker compose logs prometheus, correct the file, and retry. A reload does not create a missing target, repair DNS, or change the Compose network.
Docker daemon metrics are a separate path
Docker can expose metrics about the Docker daemon itself through the daemon’s metrics-addr setting, commonly 127.0.0.1:9323. Docker documents that metric names are still in active development and that this endpoint currently monitors Docker itself, not the applications running in containers.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Do not confuse three different endpoints:
prometheus:9090is Prometheus’s own endpoint.cadvisor:8080reports container-observation data from cAdvisor.- The daemon endpoint on port 9323 reports Docker engine metrics.
Scraping the daemon endpoint from a container requires deliberate host reachability and firewall decisions. It is not a substitute for instrumenting an application or deploying an exporter.
Minimal versus fuller Compose designs
| Aspect | Prometheus-only | Prometheus plus cAdvisor and Redis |
|---|---|---|
| Services | One Prometheus service | Prometheus, cAdvisor, and Redis |
| Monitored targets | Prometheus itself unless more jobs are added | Prometheus and cAdvisor by default; Redis needs an exporter for Redis metrics |
| Container coverage | No automatic container telemetry | cAdvisor supplies container-oriented telemetry |
| Scrape intervals | 15-second global default | 15-second default, with cAdvisor at 5 seconds in the example |
| Persistence | Named volume at /prometheus |
The same Prometheus volume; add a separate volume for Grafana if used |
| Published ports | 9090 | 9090, 8080, and 6379 in the template; bind them narrowly where possible |
| Operational complexity | Lowest; easiest to validate | More host mounts, permissions, services, and failure points |
There is no universal performance number to attach to either design. Storage use and scrape load depend on target count, label cardinality, interval, retention, and metric volume. Choose the smallest stack that answers the monitoring question you actually have.
Troubleshooting by symptom
Prometheus exits immediately
- Run
docker compose logs prometheusand look for a YAML parse error, an unreadable mounted file, or an invalid command-line option. - Confirm the host path
./prometheus.ymlexists. A typo can cause Docker to mount an unexpected directory or file type. - Run
docker compose configto catch indentation and interpolation errors before restarting.
The target is down
- Use the Compose service DNS name and internal port, such as
cadvisor:8080; never substitute the host’slocalhost. - Check that the target service is running with
docker compose ps. - Read the target’s logs and test its endpoint from an appropriate container context.
- Check the target’s port. A published host port is not required for Prometheus to reach a service on the shared Compose network.
The target is up but no historical data appears
- Confirm the query’s time range includes the period after the target became healthy.
- Check that the Prometheus data path is mounted at
/prometheus. - Inspect available disk space and Prometheus logs for storage errors.
- Verify that you did not recreate the project with
docker compose down -v.
Port 9090, 8080, 6379, or 3000 is already in use
Change only the host side of the mapping, for example 127.0.0.1:19090:9090. Keep the container-side port unchanged unless you also change the target address inside the Compose network. Then open the new host port in your browser.
cAdvisor shows permission or missing-metric errors
Check the Linux host mounts, Docker runtime mode, and security policy. Rootless Docker and non-Linux environments can expose less host information than the standard privileged-style example expects. A running cAdvisor container is not proof that every container metric is available.
A reload appears to do nothing
Confirm that --web.enable-lifecycle is present, that the POST reached the Prometheus container, and that the edited file is the same file mounted at /etc/prometheus/prometheus.yml. Read the logs for a rejected configuration; Prometheus retains the last valid configuration when the new one fails validation.
Quick Recap
Security and maintenance checklist
- Bind development interfaces to
127.0.0.1or place them behind controlled access. - Do not expose Prometheus, cAdvisor, Redis, or Grafana directly to the public internet without network controls and authentication where appropriate.
- Mount configuration and rule files read-only.
- Use explicit image tags in production and review upgrades before applying them.
- Back up the Prometheus volume and test that a restore is possible.
- Review labels and target counts as the stack grows; high-cardinality labels can increase storage and query cost.
- Keep application metrics, cAdvisor metrics, and Docker-daemon metrics as separate monitoring paths so each alert identifies the correct layer.
End-to-end setup checklist
- Place
compose.ymlandprometheus.ymlin one project directory. - Mount the configuration at
/etc/prometheus/prometheus.yml. - Mount a named volume or bind mount at
/prometheus. - Use service DNS names such as
prometheus:9090andcadvisor:8080in scrape jobs. - Run
docker compose config, thendocker compose up -d. - Check
docker compose ps, container logs, the Prometheus interface, the targets page, and each target’s/metricsendpoint. - Only after the basic stack is healthy, add cAdvisor, Grafana, exporters, rules, and additional application targets.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




