Deploy a Monitoring Stack with Docker
Orchestrate Prometheus and Grafana locally using Docker Compose.
Introduction
30 Second Summary
Every time a website goes down or a server runs out of memory, the first question is always: why didn't we catch this sooner? Monitoring tools give teams real-time visibility into what their systems are doing.
In this project, you will deploy a monitoring stack using Docker Compose, configure Prometheus to collect metrics, and build a Grafana dashboard to visualize them.
What You'll Build
A local monitoring stack consisting of Prometheus and Grafana running as Docker containers, orchestrated by Docker Compose with a single command.
By the end of this project, you'll have:
- A Docker Compose file defining multiple services, volumes, and a custom network.
- A running Prometheus instance scraping live metrics from its own endpoint.
- A custom Grafana dashboard displaying real-time data through PromQL queries.
- Secret Mission: Add Node Exporter to your stack to collect real system metrics and import a community dashboard.
Are there any prerequisites?
None. This project includes full setup instructions for all required tools, including Docker Desktop. You just need a Windows PC.
Install and Verify Docker Desktop
To deploy a monitoring stack, you need a way to run multiple services (Prometheus and Grafana) together on your machine without manually installing each one. Docker solves this by packaging each service into a self-contained unit called a container.
In this step, you will install Docker Desktop on your Windows machine and set up the folder structure for your monitoring project.
In this step, get ready to:
- Install Docker Desktop with the WSL 2 backend.
- Verify Docker and Docker Compose are working from your terminal.
- Create the project folder structure for your monitoring stack.
Download and install Docker Desktop
Docker Desktop is the application that runs the Docker engine on Windows. It includes everything you need: the Docker engine, the Docker Compose plugin, and a graphical dashboard for managing containers.
- Open your web browser and go to the Docker Desktop for Windows install page.
- Click the download button at the top of the page to download Docker Desktop Installer.exe.
- Double-click Docker Desktop Installer.exe to run the installer.
- When the installer asks which installation mode you prefer, choose **per-user installation** (recommended).
- On the Configuration page, ensure the Use WSL 2 instead of Hyper-V option is selected.
- Click through the remaining prompts to complete the installation.
What is WSL 2?
WSL 2 (Windows Subsystem for Linux 2) is a lightweight Linux environment built into Windows. Docker uses it to run Linux containers on your machine without needing a full virtual machine.
The per-user installation mode does not require administrator privileges. Enabling WSL 2 for the first time is a one-time, per-machine operation that may require admin credentials.
Will the installer ask me to restart?
The installer may prompt you to restart your computer if it needs to enable the WSL feature for the first time. If it does, save your work, restart, and then continue from the next bullet.
- After installation completes, press the Windows key to open your search bar.
- Type Docker Desktop and press Enter to open it.
- Wait for Docker Desktop to finish starting up. You will see a whale icon appear in your system tray (bottom-right corner of your taskbar) when it is ready.
What if Docker Desktop asks me to accept a license?
On first launch, Docker Desktop may show a subscription service agreement. Accept the agreement to continue. Docker Desktop is free for personal use and learning.
Docker Desktop won't start?
Make sure hardware virtualization is enabled in your BIOS/UEFI settings. This setting is sometimes called Intel VT-x or AMD-V.
If you see an error about WSL not being installed, open PowerShell as administrator and run wsl --install, then restart your computer.
Help me troubleshoot why Docker Desktop won't start on my Windows machine.
Verify Docker is installed correctly
With Docker Desktop running, you can confirm everything is working by checking the version numbers from your terminal.
- Open a terminal by pressing the Windows key, typing PowerShell, and pressing Enter.
- Check that Docker is available by running this command:
docker --version
- Check that Docker Compose is available by running this command:
docker compose version
Both commands should print a version string.
✔️ I see version numbers for both commands
Docker and Docker Compose are ready to go. You will also see the whale icon in your system tray confirming Docker Desktop is running.
ⓧ I see an error or nothing happens
- Make sure Docker Desktop is fully started. Look for the whale icon in your system tray. If it is animating, Docker is still loading.
- Close your PowerShell window and reopen it. Docker adds itself to your system PATH during installation, but existing terminal sessions may not pick it up until reopened.
- If you still see an error, restart your computer and try again after Docker Desktop finishes starting.
Help me figure out why docker --version is not working in my terminal after installing Docker Desktop on Windows.
Create the project folder structure
Your monitoring stack needs a specific folder layout. Configuration files for Prometheus and Grafana will live in their own subdirectories.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create the project folder and move into it by running these commands:
mkdir monitoring-stack
cd monitoring-stack
- Create the subdirectories for Prometheus and Grafana configuration by running these commands:
mkdir prometheus
mkdir grafana\provisioning\datasources
What does this folder structure do?
- prometheus/ will hold the Prometheus configuration file that tells it what to monitor.
- grafana/provisioning/datasources/ will hold a file that automatically connects Grafana to Prometheus on startup.
- Confirm the folders exist by running:
ls
You should see grafana and prometheus listed in the output.
Don't see both folders?
Make sure you ran the mkdir commands from inside the monitoring-stack folder. Run pwd to check your current location.
If you accidentally created folders in the wrong place, delete them and start from the cd ~/Desktop step again.
Help me fix my project folder structure for the monitoring-stack project.
Your environment is ready and your project folder is waiting. Next up, you will write the Docker Compose file that defines your entire monitoring stack.
Write the Docker Compose File
Your environment is ready with Docker Desktop running and your project folders in place. Now it's time to define the infrastructure itself.
Docker Compose uses a single YAML file to describe your entire multi-container application. Instead of running multiple docker run commands with long flag lists, you declare everything in one place and launch it all together.
In this step, get ready to:
- Create a docker-compose.yml file defining your monitoring services, volumes, and network.
- Create a .env file to store Grafana login credentials.
Create the Docker Compose file
This file will define two services (Prometheus and Grafana), two named volumes for persistent data, and a custom network so the containers can talk to each other.
- Open your text editor (Notepad or any editor of your choice).
- Create a new file and save it as docker-compose.yml inside your monitoring-stack folder.
- Start by adding the Prometheus service definition. Paste this as the beginning of your file:
services:
prometheus:
image: prom/prometheus:v3.10.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=15d"
- "--web.enable-lifecycle"
networks:
- monitoring
What does this code do?
- services: is the top-level key where you define each container in your stack.
- image: prom/prometheus:v3.10.0 tells Docker to pull the Prometheus image at this exact version tag.
- ports: "9090:9090" maps port 9090 on your machine to port 9090 inside the container, so you can reach Prometheus in your browser.
- volumes: mounts your local config file into the container (:ro means read-only) and creates a named volume for Prometheus data storage.
- command: overrides the default startup command with flags for the config path, storage location, data retention (15 days), and enabling the lifecycle API.
- networks: connects this service to the custom monitoring network.
You won't see a change yet. The next chunk adds the Grafana service.
- Below the prometheus service (still inside the services: block), add the Grafana service definition:
grafana:
image: grafana/grafana:12.4.1
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-changeme}
- GF_USERS_ALLOW_SIGN_UP=false
depends_on:
- prometheus
networks:
- monitoring
What does this code do?
- image: grafana/grafana:12.4.1 pulls the Grafana dashboard tool at a pinned version.
- environment: passes credentials from your .env file into the container. The ${VAR:-default} syntax provides a fallback value if the variable is missing.
- depends_on: prometheus tells Docker Compose to start Prometheus before Grafana.
- The volumes: section mounts a named volume for Grafana's persistent data and your provisioning folder for auto-configuring datasources later.
You won't see a change yet. The next chunk completes the file with volumes and network definitions.
- Below the services: block (at the same indentation level as services:), add the volumes and network definitions:
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
What does this code do?
volumes: declares two named volumes. Named volumes persist data even when containers are removed, so your metrics history and dashboard configurations survive restarts.
networks: creates a custom bridge network called monitoring. Services on the same network can reach each other by service name. This is why Grafana connects to Prometheus using http://prometheus:9090 instead of http://localhost:9090.
- Save docker-compose.yml.
- Confirm the file exists by running this in your terminal:
dir
You should see docker-compose.yml listed in the output alongside your prometheus and grafana folders.
File not showing up?
- Make sure you saved the file inside the monitoring-stack folder, not inside a subfolder like prometheus.
- Check that the filename is exactly docker-compose.yml with no extra extensions. Some editors add .txt automatically. Select "All Files" as the file type when saving.
- Help me troubleshoot my docker-compose.yml file location.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
services:
prometheus:
image: prom/prometheus:v3.10.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=15d"
- "--web.enable-lifecycle"
networks:
- monitoring
grafana:
image: grafana/grafana:12.4.1
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-changeme}
- GF_USERS_ALLOW_SIGN_UP=false
depends_on:
- prometheus
networks:
- monitoring
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
Create the environment file
Your docker-compose.yml references ${GRAFANA_USER} and ${GRAFANA_PASSWORD} variables. Docker Compose automatically reads a .env file from the project folder and injects those values at startup.
- In your text editor, create a new file and save it as .env inside your monitoring-stack folder.
- Add the following credentials:
GRAFANA_USER=admin
GRAFANA_PASSWORD=changeme
What does this code do?
These two lines set the admin username and password for your Grafana instance. Docker Compose injects these values into the Grafana container's environment variables wherever your Compose file uses the ${GRAFANA_USER} and ${GRAFANA_PASSWORD} syntax.
Why use a .env file for credentials?
Keeping credentials in a separate .env file means you can share your docker-compose.yml without exposing passwords. In production, you would add .env to your .gitignore so it never gets committed to version control.
- Save .env.
- Confirm both project files exist by running:
dir
You should see .env and docker-compose.yml in the output alongside your grafana and prometheus folders.
Don't see the .env file?
- Check that your editor didn't save the file as .env.txt. When saving, select "All Files" as the file type and type .env as the filename.
- Make sure the file is in the monitoring-stack folder, not a subfolder.
- Help me find or create my .env file on Windows.
Your infrastructure is defined. Next up, you'll configure Prometheus to know what metrics to scrape and set up Grafana's datasource connection.
Configure Prometheus and Grafana
Your docker-compose.yml is ready to orchestrate both services. However, each container needs its own configuration to know what to do once it starts.
In this step, you will create two configuration files that get mounted into the containers. Prometheus needs a file telling it which endpoints to scrape for metrics. Grafana needs a provisioning file that automatically connects it to Prometheus as a data source on first boot.
In this step, get ready to:
- Create a Prometheus configuration file that defines scrape targets.
- Create a Grafana datasource provisioning file that auto-connects Grafana to Prometheus.
Create the Prometheus configuration
Prometheus uses a YAML configuration file to know how often to collect metrics and where to find them. Your docker-compose.yml already mounts ./prometheus/prometheus.yml into the container at /etc/prometheus/prometheus.yml.
- Create a new file called prometheus.yml inside the prometheus folder by opening your text editor and saving a file with this content:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["prometheus:9090"]
What does this configuration do?
The global section sets the default scrape interval to 15 seconds. This means Prometheus collects metrics from every target every 15 seconds.
The scrape_configs section defines a job called prometheus that targets prometheus:9090. This tells Prometheus to scrape its own metrics endpoint. The target uses the service name (not localhost) because Docker Compose services on the same network resolve each other by name.
- Save prometheus/prometheus.yml.
- Confirm the file exists by running:
dir prometheus\prometheus.yml
You should see the file listed with its size and date.
File not found?
- Make sure you saved the file inside the prometheus subfolder, not in the root monitoring-stack folder.
- Check the filename is exactly prometheus.yml with no extra extension like .txt. In Notepad, choose All Files in the Save dialog to avoid this.
Still stuck? Help me find my prometheus.yml file
Why prometheus:9090 instead of localhost:9090?
Inside a Docker Compose network, each service gets its own isolated network namespace. localhost inside the Prometheus container refers to the container itself, which is correct here. But for cross-container communication (like Grafana reaching Prometheus), services use each other's service names as hostnames.
Using the service name prometheus keeps the config consistent across all files and prepares you for multi-service setups where targets live in different containers.
Create the Grafana datasource provisioning file
Grafana supports file-based provisioning, which means you can pre-configure datasources by placing a YAML file in a specific folder. This eliminates manual setup through the UI every time Grafana restarts.
- Create a new file called prometheus.yml inside the grafana\provisioning\datasources folder by opening your text editor and saving a file with this content:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
jsonData:
timeInterval: 15s
What does this configuration do?
- name: Prometheus sets the display name in Grafana's datasource list.
- access: proxy tells Grafana to route queries through its backend server rather than the browser. This is required because the browser cannot reach prometheus:9090 (that hostname only resolves inside the Docker network).
- url: http://prometheus:9090 uses the Docker Compose service name so Grafana can find Prometheus on the shared network.
- isDefault: true makes this the default datasource for new panels.
- timeInterval: 15s matches the Prometheus scrape interval so Grafana aligns its queries correctly.
- Save grafana/provisioning/datasources/prometheus.yml.
- Confirm the file exists by running:
dir grafana\provisioning\datasources\prometheus.yml
You should see the file listed with its size and date.
File not found in the datasources folder?
- The full path from your project root is grafana\provisioning\datasources\prometheus.yml. Make sure you did not accidentally save it one level too high (e.g. inside grafana\provisioning directly).
- Check for a hidden .txt extension. In Notepad, set Save as type to All Files before saving.
Need help? Help me place my Grafana provisioning file correctly
✔️ Awesome, I've got everything!
Great. Double check that you have saved both files before moving on.
ⓧ I'd like to double check the full code
Here is the full content of each configuration file for comparison.
prometheus/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["prometheus:9090"]
grafana/provisioning/datasources/prometheus.yml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
jsonData:
timeInterval: 15s
Both services now have the configuration they need. Next up, you will launch the entire monitoring stack with a single command and verify everything connects.
Launch the Monitoring Stack
Your configuration files are in place. It's time to see Docker Compose bring your entire monitoring stack online with a single command.
In this step, you'll launch both services and verify they're communicating correctly. By the end, you'll have Prometheus scraping metrics and Grafana ready for dashboard building.
In this step, get ready to:
- Start both containers with a single Docker Compose command.
- Verify Prometheus is scraping its own metrics endpoint.
- Log in to Grafana and confirm it's accessible.
Start the stack in detached mode
The -d flag runs containers in detached mode, meaning they run in the background and free up your terminal for other commands.
- Launch the entire monitoring stack by running this command:
docker compose up -d
Docker pulls both images from Docker Hub on the first run. This may take a minute or two depending on your internet connection.
- Confirm both containers started successfully by running:
docker compose ps
You should see both prometheus and grafana listed with a status showing Up.
Containers not starting?
If you see a port conflict error, another application is using port 9090 or 3000. Close that application or stop the conflicting container before retrying.
If Docker reports that it cannot connect to the daemon, make sure Docker Desktop is running. Check your system tray for the whale icon.
If you see a YAML syntax error, double check your docker-compose.yml for correct indentation. YAML uses spaces, not tabs.
Ask for help: Why won't my Docker Compose containers start?
Check logs and verify Prometheus targets
Checking container logs lets you catch startup errors early, before you spend time debugging in the browser.
- Check the Prometheus logs for any startup errors by running:
docker compose logs prometheus
- Check the Grafana logs by running:
docker compose logs grafana
Both logs should show normal startup messages with no error lines. If you see errors, read the message carefully for hints about missing files or permission issues.
- Open your browser and navigate to http://localhost:9090/targets.
You should see a target labeled prometheus with a state of UP shown in green. This confirms Prometheus is successfully scraping its own metrics.
What does UP mean on the targets page?
The targets page shows every endpoint Prometheus is configured to scrape. A green UP means Prometheus reached that endpoint and collected metrics successfully within the last scrape interval (15 seconds, as you configured).
If it shows DOWN in red, Prometheus could not reach the endpoint. This usually points to a networking or configuration issue.
Target showing DOWN or page not loading?
If the page does not load at all, confirm the prometheus container is running with docker compose ps. If it shows as exited, check the logs for the root cause.
If the target shows DOWN, verify that prometheus/prometheus.yml has prometheus:9090 as the target (not localhost:9090). Containers communicate using service names, not localhost.
Ask for help: Why does my Prometheus target show DOWN?
Log in to Grafana
- Open a new browser tab and navigate to http://localhost:3000.
- Enter admin as the username.
- Enter changeme as the password.
- Click Sign in.
You should see the Grafana home page after signing in. These are the credentials you defined in your .env file.
Why use a .env file for credentials?
Storing credentials in a .env file keeps them separate from your docker-compose.yml. This way you can share or version-control the compose file without exposing passwords.
Docker Compose automatically reads variables from .env in the same directory and injects them into your service definitions.
Can't log in to Grafana?
If the page does not load, confirm the grafana container is running with docker compose ps. If it shows as exited, check docker compose logs grafana for errors.
If your credentials are rejected, open your .env file and verify GRAFANA_USER=admin and GRAFANA_PASSWORD=changeme are set correctly with no extra spaces.
Ask for help: Why can't I log in to Grafana with my .env credentials?
Both services are running and healthy. Next up, you'll connect Grafana to Prometheus and build a live dashboard with real metrics.
Build a Grafana Dashboard
Your monitoring stack is running. Prometheus is scraping its own metrics and Grafana is connected to it. But right now you have no way to see those metrics at a glance.
In this step, you will build a custom Grafana dashboard with live panels. You will also learn the full stack lifecycle: stopping, restarting, and tearing down your containers.
In this step, get ready to:
- Verify the Prometheus datasource connection in Grafana.
- Create a dashboard with three monitoring panels.
- Manage the full container lifecycle: stop, restart, and tear down.
Verify the Prometheus datasource
The provisioning file you created earlier auto-configured Prometheus as a datasource in Grafana. Before building panels, you should confirm the connection is working.
- In Grafana (http://localhost:3000), click Connections in the left-side menu.
- Click Data sources.
- Click the Prometheus entry in the list.
- Scroll to the bottom and click Save & test.
You should see a green banner with the message: Successfully queried the Prometheus API.
Why test if it was auto-provisioned?
File-based provisioning sets up the datasource configuration at boot. Testing it confirms the live connection is healthy. It proves Grafana can actually reach the Prometheus container over the Docker network.
Seeing an error instead of the success message?
Make sure both containers are running. Switch to your terminal and run docker compose ps to check their status.
If the Prometheus container is not running, run docker compose up -d to restart it, then try Save & test again.
Help me troubleshoot my Prometheus datasource connection in Grafana. Both containers should be on the same Docker network called monitoring.
Create a dashboard with three panels
Now for the fun part. You will create a dashboard with three panels that visualize live PromQL queries against your Prometheus instance.
- Click Dashboards in the left-side menu.
- Click New and select New Dashboard.
- On the empty dashboard, click + Add visualization.
- Select Prometheus as the data source.
You are now in the panel editor. This is where you write queries and choose how to display the results.
What is PromQL?
PromQL is Prometheus's query language. You use it to select and aggregate time series data. It is how you ask Prometheus questions like "are my services up?" or "how many requests per second am I getting?"
Start with Panel 1: a stat panel showing whether Prometheus is healthy.
- In the query editor at the bottom of the panel editor, enter the metric:
- up
- Click Run queries (or press Shift+Enter) to refresh the data.
- In the visualization list on the right, select Stat.
- Under Panel options, set the title to Service Health (or any name you prefer).
- Click Back to dashboard in the top-left corner.
What does the up metric show?
The up metric is automatically generated by Prometheus for every scrape target. A value of 1 means the target is healthy and responding. A value of 0 means Prometheus could not reach it.
Now add Panel 2: a time series panel showing the number of active time series.
- Click Add in the dashboard header and select Visualization.
- In the query editor, enter the metric:
- prometheus_tsdb_head_series
- Click Run queries.
- In the visualization list, select Time series.
- Set the panel title to Active Time Series.
- Click Back to dashboard.
What does prometheus_tsdb_head_series track?
This metric counts how many distinct time series Prometheus is currently tracking in memory. As you add more scrape targets or labels, this number grows. It is a useful indicator of Prometheus's workload.
Finally, add Panel 3: a time series panel showing the HTTP request rate to Prometheus.
- Click Add in the dashboard header and select Visualization.
- In the query editor, enter the following PromQL expression:
- rate(prometheus_http_requests_total[5m])
- Click Run queries.
- In the visualization list, select Time series.
- Set the panel title to HTTP Request Rate.
- Click Back to dashboard.
What does rate() do in PromQL?
The rate() function calculates the per-second average rate of increase over a time window. rate(prometheus_http_requests_total[5m]) tells you how many HTTP requests per second Prometheus received, averaged over the last 5 minutes.
Now save the dashboard so your panels persist across container restarts.
- Click Save dashboard in the top-right corner.
- Enter a title such as Monitoring Stack Overview.
- Click Save.
You should now see all three panels on your dashboard, each displaying live data from Prometheus.
Panels showing No data?
Make sure the time range in the top-right corner of the dashboard is set to at least "Last 15 minutes" so Prometheus has time to collect data points.
Double-check that you selected Prometheus as the data source in each panel's query editor. If a different datasource is selected, the queries will not find any metrics.
Help me troubleshoot why my Grafana panels are showing no data when querying Prometheus metrics like up and prometheus_tsdb_head_series.
Manage the stack lifecycle
In production, you regularly stop, restart, and tear down container stacks. Docker Compose gives you commands for each stage of this lifecycle.
- Stop the stack without removing anything by running this command:
docker compose stop
What does docker compose stop do?
This stops all running containers but keeps them (and their configuration) intact. The named volumes preserving your Grafana dashboard and Prometheus data are untouched.
- Restart the stack by running:
docker compose start
- Open http://localhost:3000 in your browser again.
- Navigate to your saved dashboard.
Your dashboard and all three panels are still there. The data persisted because it lives in Docker volumes, not inside the containers themselves.
Why does data persist across restarts?
Docker volumes exist independently of containers. When you run docker compose stop and docker compose start, the containers reconnect to the same prometheus_data and grafana_data volumes. Nothing is lost.
When you are done and want to remove containers and networks, use one of these tear-down commands:
- Remove containers and networks but keep your data by running:
docker compose down
- Or remove everything including volumes (all data will be deleted) by running:
docker compose down -v
What is the difference between these two commands?
Running docker compose down removes containers and the custom network, but your named volumes (prometheus_data, grafana_data) stay intact. You can bring the stack back up later and your dashboards and metrics history will still be there.
Adding the -v flag also removes named volumes, permanently deleting all stored data. Use this when you want a completely clean slate.
Dashboard gone after restarting?
If you used docker compose down -v instead of docker compose stop, the volumes were deleted. The -v flag removes all named volumes along with their data.
To test data persistence safely, always use docker compose stop followed by docker compose start. Only use docker compose down -v when you intentionally want to wipe everything.
Help me understand the difference between docker compose stop, docker compose down, and docker compose down -v for managing my container lifecycle.
Secret mission
Add Node Exporter for Real System Metrics
Your monitoring stack is running, but it only watches Prometheus itself. What if you could collect real system metrics like CPU usage, memory consumption, and disk activity? In this secret mission, you'll extend your stack with Node Exporter and import a production-grade community dashboard.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. The Docker containers, images, and volumes consume disk space but nothing else.
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Docker containers: prometheus, grafana, node-exporter.
- Docker images pulled: prom/prometheus:v3.10.0, grafana/grafana:12.4.1, prom/node-exporter:v1.10.2.
- Named volumes: prometheus_data and grafana_data.
- Local project folder: monitoring-stack (contains all config files and subdirectories).
Keep everything running
No action needed. Choose this if you are still actively building or plan to explore the dashboards more.
- Your stack uses minimal CPU and memory when idle.
- Containers will restart automatically after a reboot thanks to the restart: unless-stopped policy.
- If the containers are currently stopped, start them again by running docker compose start from your monitoring-stack folder.
Pause - I'll come back to this later
Shut down running processes to free up memory. Your configuration files and volume data are preserved so you can pick up where you left off.
- Stop all containers by running this command from your monitoring-stack folder:
docker compose stop
- When you are ready to resume, run docker compose start from the same folder.
- All your dashboards and scraped metrics will still be there.
Delete - I don't want to use this again
Remove all project resources and start fresh. This removes containers, networks, volumes, and all stored data permanently.
- Remove containers, networks, and volumes by running this command from your monitoring-stack folder:
docker compose down -v
- Remove unused Docker images by running:
docker image prune
- Type y and press Enter when prompted to confirm.
- Navigate up one level and delete the project folder:
cd ..
Remove-Item -Recurse -Force monitoring-stack
- Confirm the folder is gone by running dir and checking that monitoring-stack no longer appears.
Nice Work!
Nice Work!
You just deployed a production-grade monitoring stack from scratch using Docker Compose. You went from zero to a fully running Prometheus and Grafana setup, orchestrated by a single YAML file.
You've learned how to:
- Write a Docker Compose file from scratch, defining services, volumes, and a custom network to orchestrate a multi-container stack.
- Launch and manage the full container lifecycle with docker compose up, stop, start, and down.
- Build a custom Grafana dashboard with live PromQL queries pulling real metrics from Prometheus.
- Secret Mission: Extended the stack with Node Exporter and imported a community dashboard to monitor real system metrics.
Ready to quiz yourself?