Installing Prometheus on Ubuntu
Prometheus is a monitoring tool that collects numbers (called metrics) from your servers and applications, stores them over time, and lets you query and alert on them. It works by “scraping” — meaning it regularly visits a web address on each target and reads the metrics it exposes. In this guide you will install Prometheus from the official binary on Ubuntu, run it as a proper background service, point it at a real scrape target, and confirm in the web UI that it is collecting data. This matters because once Prometheus is running and scraping itself, you have a foundation you can extend to monitor your whole fleet.
When to install Prometheus this way
There are a few ways to run Prometheus. Picking the right one keeps your setup simple and easy to maintain.
| Method | When to use it | When NOT to use it |
|---|---|---|
| Official binary + systemd (this guide) | A real Ubuntu server you control and want long-term. Full control over version and config. | You want zero-maintenance hands-off hosting. |
apt install prometheus | Quick test on a throwaway box. | Production — the Ubuntu package often lags well behind the latest release. |
| Docker container | You already run everything in containers. | A single plain server where Docker adds overhead you don’t need. |
| Hosted (Grafana Cloud, AWS AMP) | You don’t want to manage the server at all. | You need data to stay fully on your own machine. |
We use the official binary with systemd (the standard Ubuntu service manager that starts, stops, and restarts background programs) because it gives you the current version and behaves like any other well-run Linux service.
Step 1 — Create a dedicated user
Running Prometheus as a normal login user (or worse, as root, the all-powerful admin account) is a security risk. If the process is ever compromised, the attacker gets whatever that account can do. The fix is a system user — a locked-down account with no login shell and no home directory, used only to run this one service.
sudo useradd --no-create-home --shell /usr/sbin/nologin prometheus
This creates a user named prometheus that nobody can log in as. --shell /usr/sbin/nologin blocks interactive logins, and --no-create-home skips making a home folder it doesn’t need.
Step 2 — Download and install the binary
Go to the official releases page, copy the latest Linux AMD64 download link, and fetch it. At the time of writing the current line is the 3.x series.
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v3.4.1/prometheus-3.4.1.linux-amd64.tar.gz
tar -xvf prometheus-3.4.1.linux-amd64.tar.gz
cd prometheus-3.4.1.linux-amd64
Output:
prometheus-3.4.1.linux-amd64/
prometheus-3.4.1.linux-amd64/prometheus
prometheus-3.4.1.linux-amd64/promtool
prometheus-3.4.1.linux-amd64/LICENSE
prometheus-3.4.1.linux-amd64/NOTICE
Now move the two programs into a system path and create the folders Prometheus uses. /usr/local/bin is where you put binaries you installed by hand. /etc/prometheus holds config, and /var/lib/prometheus holds the stored metrics (this is the time-series database, or TSDB — the on-disk store of all those numbers over time).
sudo cp prometheus promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo cp -r consoles console_libraries /etc/prometheus/
Give the prometheus user ownership of its directories so the service can read its config and write its data.
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
Confirm it installed correctly:
prometheus --version
Output:
prometheus, version 3.4.1 (branch: HEAD, revision: ...)
build user: root@...
go version: go1.24.3
Step 3 — Write the configuration file
Prometheus reads a single YAML file, prometheus.yml. YAML is a plain-text format where indentation (spaces, never tabs) defines structure. Two settings matter most here:
scrape_interval— how often Prometheus pulls metrics from each target.scrape_configs— the list of things to scrape. Each entry is a job (a named group of targets) with one or moretargets(host:port addresses).
Create the file:
sudo nano /etc/prometheus/prometheus.yml
Paste this in. It tells Prometheus to scrape itself, which every install can do because Prometheus exposes its own metrics on port 9090 at the path /metrics.
global:
scrape_interval: 15s # how often to scrape every target by default
evaluation_interval: 15s # how often to evaluate alerting rules
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
Before you start the service, validate the file with promtool, the checking tool that shipped alongside Prometheus. Catching a typo now is far easier than debugging a service that won’t start.
promtool check config /etc/prometheus/prometheus.yml
Output:
Checking /etc/prometheus/prometheus.yml
SUCCESS: 1 rule files found
SUCCESS: /etc/prometheus/prometheus.yml is valid prometheus config file syntax
Step 4 — Create the systemd service
A systemd unit file describes how to run a program as a managed background service. Create one so Prometheus starts on boot and restarts if it crashes.
sudo nano /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus Monitoring
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090
[Install]
WantedBy=multi-user.target
Restart=on-failure means systemd brings the service back if it dies. User=prometheus runs it as the locked-down account from Step 1.
Reload systemd so it sees the new file, then enable (start on boot) and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl status prometheus
Output:
● prometheus.service - Prometheus Monitoring
Loaded: loaded (/etc/systemd/system/prometheus.service; enabled)
Active: active (running) since Mon 2026-06-15 10:12:04 UTC; 3s ago
Main PID: 4821 (prometheus)
active (running) means it is up. If it shows failed, run sudo journalctl -u prometheus -e to read the error log.
Step 5 — Open the firewall (only if needed)
If you want to reach the web UI from another machine and you use UFW (Uncomplicated Firewall, Ubuntu’s simple firewall front-end), allow port 9090. Restrict it to a trusted address rather than opening it to the world.
sudo ufw allow from 203.0.113.10 to any port 9090 proto tcp
Security gotcha: the Prometheus web UI has no built-in login. Anyone who can reach port
9090can read all your metrics and shut Prometheus down via its API. Never expose it to the public internet directly — keep it behind a firewall, a VPN, or a reverse proxy that handles authentication.
Step 6 — View targets and confirm self-scraping
Open http://your-server-ip:9090 in a browser. Then go to Status → Targets in the top menu. You should see one target, the prometheus job, with a green UP state. That green status is your proof that scraping works end to end.
You can also verify from the command line. Query the built-in up metric, which is 1 for every healthy target:
curl -s 'http://localhost:9090/api/v1/query?query=up'
Output:
{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"up","instance":"localhost:9090","job":"prometheus"},"value":[1718446324,"1"]}]}}
The trailing "1" confirms Prometheus is successfully scraping itself.
Best Practices
- Always run
promtool check configbefore reloading or restarting — it catches YAML mistakes that would otherwise crash the service. - Reload config without downtime using
sudo systemctl reload prometheus(orkill -HUP) instead of a full restart after editingprometheus.yml. - Never expose port
9090to the public internet; put it behind a firewall, VPN, or authenticating reverse proxy. - Pin a specific version in your download URL so rebuilds are reproducible, and review release notes before upgrading.
- Set a sensible retention with
--storage.tsdb.retention.time=30dso the database doesn’t fill your disk over time. - Keep
scrape_intervalat 15s to start; only lower it if you genuinely need finer resolution, since it increases storage and load.