Installing Prometheus — running Prometheus, Pushgateway, and Alertmanager on Linux with systemd
How to install Prometheus, Pushgateway, and Alertmanager from binaries on Linux and register them as systemd services. We cover the dedicated user and directory layout, the unit files for all three components, and the key runtime options — especially --web.listen-address, which sets the bind IP.
The Prometheus ecosystem ships as single static binaries, so installation is simple. Without a package manager you just download a tarball and extract it, and you leave operation to systemd. This post is a hands-on guide to putting Prometheus, Pushgateway, and Alertmanager on Linux, registering them as systemd services, and the runtime options you must know — especially the bind IP address.
What this post covers:
- The install method, standard directory layout, and dedicated user
- Installing Prometheus / Pushgateway / Alertmanager
- Writing systemd unit files for all three
- Key runtime options and
--web.listen-address(the bind IP) - Connecting the three, verifying, and systemd hardening
1. Install method and directory layout
Distro packages exist, but the official binary (tarball) install is the most recommended — it makes version control easy. Deciding a standard layout up front keeps the unit files clean.
| Purpose | Path |
|---|---|
| Executables | /usr/local/bin/ |
| Config files | /etc/prometheus/, /etc/alertmanager/ |
| Data (TSDB/state) | /var/lib/prometheus/, /var/lib/alertmanager/, /var/lib/pushgateway/ |
| Run account | dedicated system user prometheus (no login) |
Default ports are Prometheus 9090 · Pushgateway 9091 · Alertmanager 9093 (cluster 9094).
2. Common prep — dedicated user and directories
To avoid running the daemons as root, create a non-login system account and prepare the directories.
# dedicated system user (no home, no shell)
sudo useradd --system --no-create-home --shell /usr/sbin/nologin prometheus
# config/data directories
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo mkdir -p /etc/alertmanager /var/lib/alertmanager
sudo mkdir -p /var/lib/pushgateway
sudo chown -R prometheus:prometheus /var/lib/prometheus /var/lib/alertmanager /var/lib/pushgateway3. Installing Prometheus
Check the latest version at prometheus.io/download (replace VERSION below), then download and place the binaries and default config.
VERSION=3.2.1 # replace with the latest stable version
cd /tmp
curl -LO https://github.com/prometheus/prometheus/releases/download/v${VERSION}/prometheus-${VERSION}.linux-amd64.tar.gz
tar xzf prometheus-${VERSION}.linux-amd64.tar.gz
cd prometheus-${VERSION}.linux-amd64
# binaries
sudo cp prometheus promtool /usr/local/bin/
# default config (only if absent)
sudo cp prometheus.yml /etc/prometheus/prometheus.yml
sudo chown -R prometheus:prometheus /etc/prometheuspromtool is the config/rule validation tool, so install it too.
4. Installing the Pushgateway
VERSION=1.11.0 # replace with the latest version
cd /tmp
curl -LO https://github.com/prometheus/pushgateway/releases/download/v${VERSION}/pushgateway-${VERSION}.linux-amd64.tar.gz
tar xzf pushgateway-${VERSION}.linux-amd64.tar.gz
sudo cp pushgateway-${VERSION}.linux-amd64/pushgateway /usr/local/bin/5. Installing Alertmanager
VERSION=0.28.0 # replace with the latest version
cd /tmp
curl -LO https://github.com/prometheus/alertmanager/releases/download/v${VERSION}/alertmanager-${VERSION}.linux-amd64.tar.gz
tar xzf alertmanager-${VERSION}.linux-amd64.tar.gz
cd alertmanager-${VERSION}.linux-amd64
sudo cp alertmanager amtool /usr/local/bin/
sudo cp alertmanager.yml /etc/alertmanager/alertmanager.yml
sudo chown -R prometheus:prometheus /etc/alertmanagerEach component has a different version number (Prometheus, Pushgateway, and Alertmanager release independently). Check each one's latest version when downloading.
6. Registering systemd services
Create three unit files. The meaning of the ExecStart options is explained in the next section.
/etc/systemd/system/prometheus.service:
[Unit]
Description=Prometheus
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 \
--storage.tsdb.retention.time=30d \
--web.listen-address=0.0.0.0:9090 \
--web.enable-lifecycle
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target/etc/systemd/system/pushgateway.service:
[Unit]
Description=Prometheus Pushgateway
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/pushgateway \
--web.listen-address=0.0.0.0:9091 \
--persistence.file=/var/lib/pushgateway/metrics \
--persistence.interval=5m
[Install]
WantedBy=multi-user.target/etc/systemd/system/alertmanager.service:
[Unit]
Description=Prometheus Alertmanager
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/alertmanager \
--config.file=/etc/alertmanager/alertmanager.yml \
--storage.path=/var/lib/alertmanager \
--web.listen-address=0.0.0.0:9093 \
--cluster.listen-address=
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.targetRegister and start:
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus pushgateway alertmanager
sudo systemctl status prometheus
journalctl -u prometheus -f # follow logs7. Key options — especially the bind IP
Bind IP: --web.listen-address
For all three components, the HTTP server's --web.listen-address decides which IP and port to bind to. The format is host:port, and the host part is the interface to bind to.
--web.listen-address=0.0.0.0:9090 # all interfaces (default-like) — reachable from anywhere
--web.listen-address=127.0.0.1:9090 # localhost only — reachable only from the same host
--web.listen-address=10.0.0.5:9090 # bind to a specific NIC/IP — expose only the internal IP
--web.listen-address=:9090 # omitting host = all interfaces (same as 0.0.0.0)
--web.listen-address=[::1]:9090 # IPv6 localhostHow to choose:
0.0.0.0(all interfaces) — the most open. On a server with a public IP, leaving it as-is exposes it directly to the internet. Since Prometheus/Pushgateway/Alertmanager have no authentication by default, this is dangerous.127.0.0.1(localhost) — suitable when placing them behind a reverse proxy (Nginx, etc.) on the same host that handles TLS/auth.- A specific internal IP (
10.0.0.5) — when you want to expose only the management/private NIC and keep the public NIC closed.
Security advice: in production, default to binding to an internal IP or
127.0.0.1+ a firewall, and put a reverse proxy (TLS/auth) in front when external access is needed. The Prometheus family also supports native TLS/basic auth via--web.config.file.
Alertmanager's second bind: --cluster.listen-address
Besides --web.listen-address for the HTTP UI/API, Alertmanager has a separate --cluster.listen-address (default 0.0.0.0:9094) for gossip between HA cluster members.
- For a single node, clustering isn't needed, so disable it with an empty value:
--cluster.listen-address= - For multiple nodes (HA), bind to each node's IP and point at the others with
--cluster.peer=<other-node:9094>.
Other key options
| Component | Option | Meaning |
|---|---|---|
| Prometheus | --config.file | config file path |
| Prometheus | --storage.tsdb.path | TSDB data directory |
| Prometheus | --storage.tsdb.retention.time | retention period (default 15d). e.g. 30d |
| Prometheus | --storage.tsdb.retention.size | retention size cap. e.g. 50GB |
| Prometheus | --web.enable-lifecycle | allow /-/reload·/-/quit over HTTP |
| Prometheus | --web.external-url | external URL behind a reverse proxy |
| Pushgateway | --persistence.file | persist metrics across restarts (file path) |
| Pushgateway | --persistence.interval | persistence interval (default 5m) |
| Alertmanager | --storage.path | path for alert state (silences, etc.) |
| Alertmanager | --data.retention | state retention period (default 120h) |
Enabling --web.enable-lifecycle lets you reload after config changes without a restart.
curl -X POST http://localhost:9090/-/reload8. Connecting the three components
Configure prometheus.yml so Prometheus scrapes the Pushgateway, sends alerts to Alertmanager, and reads rule files.
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "/etc/prometheus/rules/*.yml"
alerting:
alertmanagers:
- static_configs:
- targets: ["127.0.0.1:9093"]
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["127.0.0.1:9090"]
- job_name: "pushgateway"
honor_labels: true # preserve pushed job/instance labels (required)
static_configs:
- targets: ["127.0.0.1:9091"]After configuring, validate and reload:
promtool check config /etc/prometheus/prometheus.yml
curl -X POST http://localhost:9090/-/reload9. Verifying
Each component exposes health/ready endpoints.
curl -s http://localhost:9090/-/healthy # Prometheus
curl -s http://localhost:9090/-/ready
curl -s http://localhost:9091/-/healthy # Pushgateway
curl -s http://localhost:9093/-/healthy # Alertmanager
# check which ports are listening
sudo ss -ltnp | grep -E '9090|9091|9093'Open http://<server-ip>:9090 (Prometheus), :9091 (Pushgateway), and :9093 (Alertmanager) in a browser to check the UIs. If every target is UP on Prometheus's Status → Targets, you're good.
10. (Bonus) systemd hardening
Beyond the dedicated user, narrowing the daemon's filesystem access with systemd security options is wise. Add to the [Service] section.
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/prometheus # allow writes only to the data directoryProtectSystem=strict makes the whole system read-only, so the key is to carve out the data directory via ReadWritePaths. (Do the same for the Pushgateway/Alertmanager with their respective data paths.)
Wrapping up
Installing the Prometheus stack is ultimately a repetition of place the binary → dedicated user/directories → systemd unit → tune options. The first thing to check in production is the bind IP. Since these components have no authentication by default, narrowing --web.listen-address to 127.0.0.1 or an internal IP, and fronting them with a firewall and a reverse proxy (TLS/auth), is a safe starting point.
As a next step, add exporters like node_exporter on top, and connect the alert rules and Alertmanager routing covered in earlier posts to complete the "collect → alert → notify" pipeline.