Install Prometheus on Ubuntu as a systemd service

In Lesson 1 you ran Prometheus by hand in the foreground, and it stopped the moment you pressed Ctrl+C. This lesson shows you how to install Prometheus on Ubuntu the way it runs at work: a checked download, its own locked-down user, fixed folders for config and data, and a systemd service that starts at boot and restarts if it crashes.

You’ll also learn the daily habits that keep it healthy: testing the config with promtool before you touch the running server, reloading without a restart, reading its logs with journalctl, and upgrading to a new version without losing your data.

Where this lesson fits

Lesson 2 of 10 in Prometheus Monitoring · Phase 1: Prometheus foundations and setup · Before this: Lesson 1, what is Prometheus · Next: Lesson 3, secure Prometheus

TimeYou needBy the end you can
About 90 minutesprom01 from Lesson 1 (snapshot “base”), internet access from the VM, an SSH session as studentPrometheus 3.16.0 running on prom01 as a systemd service, surviving reboots, with a snapshot called prom-installed

What it is, in one line

Installing Prometheus properly means putting the binary, the config and the data in fixed places, running it as its own user, and letting systemd start, stop and reload it.

In Simple Words

The new cook, the kitchen and the house rules

Imagine you hire a cook for a busy mess near a college. On the first day you let him cook in your own kitchen while you stand next to him, and that works for one meal. For a real job you give him his own kitchen, a fixed shelf for the recipe book, a fixed store room for the rations and a key that opens only those rooms, and the mess manager makes sure he turns up every morning at six.

If the recipe book changes, the manager checks the new page for mistakes first and then hands it over, and the cook carries on cooking without leaving the stove. When a better cook comes along, the manager swaps the person but keeps the same kitchen and the same store room full of rations.

Prometheus is the cook, /etc/prometheus is the recipe shelf, /var/lib/prometheus is the store room, and systemd is the mess manager.

Engineer's View

What a proper install looks like

Prometheus ships as two static Go binaries with no dependencies, so there’s no package to install and nothing to compile. The official tarball from GitHub is the source of truth; the Ubuntu archive also has a prometheus package, but it usually trails upstream by many versions and uses different paths, so this course uses the tarball.

PieceWhere it goesOwner and mode
prometheus, promtool binaries/usr/local/bin/root:root, 755
Main config prometheus.yml/etc/prometheus/prometheus:prometheus (read is enough)
Rule files (Lessons 7 and 8)/etc/prometheus/rules/*.ymlprometheus:prometheus
TSDB data: blocks, WAL, lock file/var/lib/prometheus/prometheus:prometheus, must be writable
Service unit/etc/systemd/system/prometheus.serviceroot:root, 644
Logsjournald (journalctl -u prometheus)handled by systemd

The service user is a system account with no home and the shell /usr/sbin/nologin, so nobody can log in as it and a bug in Prometheus can’t touch other users’ files. The unit passes four flags: where the config is, where the data goes, how long to keep data (15 days) and --web.enable-lifecycle, which turns on the /-/reload and /-/quit HTTP endpoints.

Prometheus reads its config at start and again when it gets a reload (SIGHUP or POST to /-/reload). If the new file is broken, the reload fails, the old config stays active and the metric prometheus_config_last_reload_successful drops to 0. A restart with a broken file is different: the process exits and nothing is monitored until you fix it.

How it works, step by step

1Every piece has one fixed home

Keeping program, settings and data apart is what makes upgrades and backups easy. You can replace the binary without touching your data, copy /etc/prometheus into Git, and back up or resize /var/lib/prometheus on its own disk later.

The drawing below follows the files from the download to the running service. Note who owns what: root owns the programs, the prometheus user owns the config and data, and systemd ties them together.

Install Prometheus on Ubuntu: where the binaries, config, data and systemd unit live on prom01

2systemd runs it as the prometheus user

When you run systemctl start prometheus, systemd reads the unit file, switches to the user and group in User= and Group=, and runs the ExecStart= line. Anything Prometheus prints goes to the journal, and if the process dies with an error, Restart=on-failure starts it again after RestartSec.

On start, Prometheus opens the data folder and creates a lock file there so a second copy can’t use the same folder. It replays the write-ahead log (WAL) to rebuild recent data in memory, loads the config and only then reports ready.

3How a config change reaches the running server

You’ll change prometheus.yml many times in this course. The safe path is always the same: edit, run promtool check config, then reload. The reload swaps in the new scrape jobs and rules without throwing away the data in memory, so there’s no gap in your graphs.

The second drawing shows that path and what happens when you skip the check.

Install Prometheus on Ubuntu: edit, promtool check, reload and verify flow for a safe config change

4Latest or LTS, and how upgrades work

Prometheus puts out a new minor release roughly every six weeks, and once a year one line is marked long-term support (LTS) and gets bug and security fixes for about a year. On 8 Nov 2026 the latest release is 3.16.0 and the LTS line is 3.13 (3.13.5), supported until the end of July 2027.

ChooseWhen it fitsTrade-off
Latest (3.16.x)Labs, teams who upgrade often, when you need a new featureEach line gets fixes only until the next release
LTS (3.13.x)Banks, hospitals, sites with strict approval for every changeNew features arrive once a year

An upgrade inside version 3 is a binary swap: stop the service, replace prometheus and promtool, start it again. The TSDB format is kept stable inside a major version, so your data stays readable, but read the release notes first and take a VM snapshot (or copy of the data folder) so you can go back.

Hands-on lab: run Prometheus 3.16 as a systemd service on prom01

Before you run any command, double-check it

Read every command twice before you press Enter. Check the target: the server name, the user, the disk or device name, the path and the sizes. These commands are for learning and were tested in our lab, not on your system. At work, run the read-only commands first, follow your change process, make sure a backup exists, and ask a second person to check anything that changes or deletes data. You run them at your own risk.

Restore prom01 to the base snapshot from Lesson 1 (or delete the test folder in your home), start it and SSH in as student. Every command in this lab runs on prom01.

1Download Prometheus and check the checksum

Run this on prom01:

VER=3.16.0
mkdir -p ~/dl && cd ~/dl
wget -q https://github.com/prometheus/prometheus/releases/download/v$VER/prometheus-$VER.linux-amd64.tar.gz
wget -q https://github.com/prometheus/prometheus/releases/download/v$VER/sha256sums.txt
sha256sum -c sha256sums.txt --ignore-missing

You should see:

prometheus-3.16.0.linux-amd64.tar.gz: OK

sha256sums.txt lists a checksum for every file in the release, and --ignore-missing tells sha256sum to check only the ones you downloaded. “OK” means the file you got is byte for byte the one the project published; “FAILED” means a broken or tampered download, so delete it and fetch it again.

✅ Checkpoint: sha256sum prints OK for prometheus-3.16.0.linux-amd64.tar.gz.

2Create the prometheus user and folders

Run this on prom01:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin prometheus
sudo mkdir -p /etc/prometheus/rules /var/lib/prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
id prometheus
ls -ld /etc/prometheus /var/lib/prometheus

You should see this (your UID and GID numbers and times will differ):

uid=996(prometheus) gid=995(prometheus) groups=995(prometheus)
drwxr-xr-x 3 prometheus prometheus 4096 Nov  8 05:20 /etc/prometheus
drwxr-xr-x 2 prometheus prometheus 4096 Nov  8 05:20 /var/lib/prometheus

--system gives a low UID (below 1000) and no password ageing, which is how service accounts are set up on Ubuntu. Your UID may be a different number, and that’s fine.

✅ Checkpoint: id prometheus works and both folders are owned by prometheus.

3Install the binaries and the first config

Paste this whole block:

cd ~/dl && tar xzf prometheus-$VER.linux-amd64.tar.gz
sudo install -m 755 -o root -g root prometheus-$VER.linux-amd64/prometheus prometheus-$VER.linux-amd64/promtool /usr/local/bin/
sudo tee /etc/prometheus/prometheus.yml >/dev/null <<'EOF'
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - /etc/prometheus/rules/*.yml

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
EOF
sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
prometheus --version

You should see:

prometheus, version 3.16.0 (branch: HEAD, revision: e2e28adeee2c5883d000fc82aca6008cfbf7e96d)
  build user:       root@880187fc0808
  build date:       20261105-15:13:16
  go version:       go1.27.3
  platform:         linux/amd64
  tags:             netgo,builtinassets

The config is shorter than the sample in the tarball on purpose, so you can read every line. global sets defaults for all jobs: scrape every 15 seconds (the built-in default is 1 minute) and run rules every 15 seconds. scrape_timeout is not set, so it stays at 10 seconds, and it must never be longer than the interval.

rule_files takes glob patterns, and an empty folder is fine, so Lessons 7 and 8 only need to drop files into /etc/prometheus/rules. Under scrape_configs each list item starting with - job_name is one job, and static_configs lists its targets as host:port. Prometheus adds /metrics and http:// itself.

✅ Checkpoint: The version check reports 3.16.0 and /etc/prometheus/prometheus.yml exists.

4Check the config with promtool

Run this on prom01:

promtool check config /etc/prometheus/prometheus.yml; echo "exit code: $?"

You should see this:

Checking /etc/prometheus/prometheus.yml
 SUCCESS: /etc/prometheus/prometheus.yml is valid prometheus config file syntax

exit code: 0

promtool parses the file the same way the server does, including any rule files it points to. Exit code 0 means valid and 1 means failed, which is handy in scripts and CI pipelines: you can block a Git merge if the check fails.

✅ Checkpoint: promtool prints SUCCESS and exit code 0.

5Write the systemd unit

Paste this whole block:

sudo tee /etc/systemd/system/prometheus.service >/dev/null <<'EOF'
[Unit]
Description=Prometheus monitoring server
Documentation=https://prometheus.io/docs/introduction/overview/
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=15d \
  --web.enable-lifecycle
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF
sudo systemd-analyze verify /etc/systemd/system/prometheus.service && echo "unit OK"

You should see this:

unit OK

The backslashes let one long ExecStart line wrap over several lines, and there must be nothing after them, not even a space. ExecReload sends SIGHUP to the main process, which Prometheus treats as “read your config again”. LimitNOFILE=65536 raises the open file limit, because a busy server keeps many block files and connections open.

Writing 15d for retention looks the same as the default, but writing it down means nobody has to guess, and Lesson 6 adds a size limit next to it.

✅ Checkpoint: systemd-analyze verify prints nothing and you see “unit OK”.

6Start it, enable it at boot and read the logs

Run this on prom01:

sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
systemctl status prometheus --no-pager -n 0
sudo journalctl -u prometheus -n 5 --no-pager

You should see this (your times and PIDs will differ, and the long log lines are shortened):

Created symlink '/etc/systemd/system/multi-user.target.wants/prometheus.service' → '/etc/systemd/system/prometheus.service'.
● prometheus.service - Prometheus monitoring server
     Loaded: loaded (/etc/systemd/system/prometheus.service; enabled; preset: enabled)
     Active: active (running) since Sun 2026-11-08 05:24:02 UTC; 4s ago
 Invocation: 4c1f9a7e2b6d4e0f8a3c5b7d9e1f2a4c
       Docs: https://prometheus.io/docs/introduction/overview/
   Main PID: 1532 (prometheus)
      Tasks: 8 (limit: 2266)
     Memory: 38.6M (peak: 39.1M)
        CPU: 312ms
     CGroup: /system.slice/prometheus.service
             └─1532 /usr/local/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --storage.tsdb.retention.time=15d --web.enable-lifecycle
Nov 08 05:24:02 prom01 prometheus[1532]: time=2026-11-08T05:24:02.497Z level=INFO source=main.go:1744 msg="Loading configuration file" filename=/etc/prometheus/prometheus.yml
Nov 08 05:24:02 prom01 prometheus[1532]: time=2026-11-08T05:24:02.497Z level=INFO source=main.go:1148 msg="TSDB retention updated" duration=15d size=0B percentage=0
Nov 08 05:24:02 prom01 prometheus[1532]: time=2026-11-08T05:24:02.498Z level=INFO source=main.go:1786 msg="Completed loading of configuration file" db_storage=29.066µs ... filename=/etc/prometheus/prometheus.yml totalDuration=796.506µs
Nov 08 05:24:02 prom01 prometheus[1532]: time=2026-11-08T05:24:02.498Z level=INFO source=main.go:1506 msg="Server is ready to receive web requests."
Nov 08 05:24:02 prom01 prometheus[1532]: time=2026-11-08T05:24:02.498Z level=INFO source=manager.go:235 msg="Starting rule manager..." component="rule manager"

enable --now does two jobs in one: it links the unit into multi-user.target so it starts at boot, and starts it right away. In the log, each line has time=, level= (INFO, WARN, ERROR), source= (the Go file and line) and msg=, so you can grep for level=ERROR when something goes wrong.

✅ Checkpoint: systemctl shows “active (running)” and “enabled”, and the journal shows “Server is ready to receive web requests”.

Install Prometheus on Ubuntu lab: Prometheus running as a systemd service on prom01
Prometheus running as a systemd service on prom01

7Check the health and ready endpoints

Run this on prom01:

curl -s http://localhost:9090/-/healthy; echo
curl -s http://localhost:9090/-/ready; echo
curl -s 'http://localhost:9090/api/v1/query?query=up'; echo
sudo ss -ltnp | grep 9090

You should see this (your timestamp and PID will differ):

Prometheus Server is Healthy.
Prometheus Server is Ready.
{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"up","instance":"localhost:9090","job":"prometheus"},"value":[1794115460.399,"1"]}]}}
LISTEN 0      4096         0.0.0.0:9090       0.0.0.0:*    users:(("prometheus",pid=1532,fd=8))

/-/healthy answers 200 as soon as the process is alive, while /-/ready waits until the WAL replay is done and queries will work. Load balancers and Kubernetes probes use them that way: healthy for “restart it if this fails”, ready for “send traffic now”. The API answer is the same up query you ran in the browser in Lesson 1, as JSON.

✅ Checkpoint: Both endpoints answer, and the API returns up with value “1”.

8Tour the web UI

On your laptop open http://192.168.56.60:9090. Prometheus 3 has a new interface; these are the pages you’ll use most.

PageWhat you find thereUse it when
QueryQuery box with Table and Graph tabs, plus the metrics explorerTrying PromQL, checking a value
AlertsAlerting rules and their state (inactive, pending, firing)Lesson 8 onwards
Status > Target healthEvery target, its state, last scrape, duration and last errorA graph is empty or up is 0
Status > ConfigurationThe config the server is running now, with defaults filled inChecking that a reload took effect
Status > Command-line flagsEvery flag and its value, including the ones you didn’t setChecking retention or listen address
Status > TSDB statusSeries count, top metrics and labels by seriesMemory is growing (Lesson 6)
Status > Runtime & build informationVersion, uptime, storage retention, reload statusAfter an upgrade

Open Status > Command-line flags and type retention in the filter: you should see storage.tsdb.retention.time set to 15d. Then open Status > Configuration and note that Prometheus has filled in scrape_timeout: 10s even though you never wrote it.

Install Prometheus on Ubuntu: Command-line flags page filtered on retention
Status > Command-line flags filtered on retention: storage.tsdb.retention.time is 15d
Install Prometheus on Ubuntu: Status > Configuration with defaults filled in” class=”wp-image-10262″ width=”1400″ height=”1124″ style=”width:100%;height:auto;border-radius:10px;” /><figcaption style=Status > Configuration: the running config, with scrape_timeout: 10s and other defaults filled in

✅ Checkpoint: You opened Target health, Configuration and Command-line flags, and retention shows 15d.

9Reload the config two ways

Change the scrape interval to 30s, check it, reload with the HTTP endpoint, then put it back and reload with systemctl.

Run this on prom01:

sudo sed -i 's/scrape_interval: 15s/scrape_interval: 30s/' /etc/prometheus/prometheus.yml
promtool check config /etc/prometheus/prometheus.yml
curl -s -X POST http://localhost:9090/-/reload && echo "reload sent"
curl -s http://localhost:9090/api/v1/status/config | grep -o 'scrape_interval: [0-9a-z]*' | head -1
sudo sed -i 's/scrape_interval: 30s/scrape_interval: 15s/' /etc/prometheus/prometheus.yml
sudo systemctl reload prometheus
curl -s http://localhost:9090/metrics | grep '^prometheus_config_last_reload_successful'

You should see:

Checking /etc/prometheus/prometheus.yml
 SUCCESS: /etc/prometheus/prometheus.yml is valid prometheus config file syntax

reload sent
scrape_interval: 30s
prometheus_config_last_reload_successful 1

The POST works only because the unit has --web.enable-lifecycle; without it Prometheus answers “Lifecycle API is not enabled.” The systemctl reload sends SIGHUP through ExecReload. sudo journalctl -u prometheus -n 4 shows a “Completed loading of configuration file” line for each reload, and the PID in systemctl status doesn’t change, so the process never restarted.

✅ Checkpoint: Status > Configuration showed 30s after the first reload and 15s after the second, and prometheus_config_last_reload_successful is 1.

10Reboot test, snapshot, and how you would upgrade

Run this on prom01:

sudo reboot
# log in again after a minute, then:
systemctl is-active prometheus; systemctl is-enabled prometheus

You should see this:

active
enabled

Shut prom01 down and take a VirtualBox snapshot called prom-installed. Lesson 3 starts from it. When a new release comes out later, the upgrade on this layout looks like this (don’t run it now, 3.16.0 is current):

NEW=3.16.1                     # the version you are moving to
cd ~/dl && wget -q https://github.com/prometheus/prometheus/releases/download/v$NEW/prometheus-$NEW.linux-amd64.tar.gz
wget -q -O sha256sums-$NEW.txt https://github.com/prometheus/prometheus/releases/download/v$NEW/sha256sums.txt
sha256sum -c sha256sums-$NEW.txt --ignore-missing
tar xzf prometheus-$NEW.linux-amd64.tar.gz
prometheus-$NEW.linux-amd64/promtool check config /etc/prometheus/prometheus.yml
sudo systemctl stop prometheus
sudo cp /usr/local/bin/prometheus /usr/local/bin/prometheus.old
sudo install -m 755 prometheus-$NEW.linux-amd64/prometheus prometheus-$NEW.linux-amd64/promtool /usr/local/bin/
sudo systemctl start prometheus
prometheus --version | head -1; curl -s http://localhost:9090/-/ready

Checking your current config with the new promtool before you stop anything catches settings that the new version no longer accepts. If the new version misbehaves, stop it, copy prometheus.old back and start again, or restore the snapshot.

✅ Checkpoint: After the reboot Prometheus is active and enabled, and prom01 has a snapshot called prom-installed.

Break It, Then Fix It

Three failures you will meet on a real server. Take a VirtualBox snapshot first (or rely on prom-installed) so you can roll back if you get lost.

1A tab sneaks into prometheus.yml

Many editors and copy-paste from chat tools put a tab where YAML expects spaces. Put one in on purpose:

Run this on prom01:

sudo cp /etc/prometheus/prometheus.yml /etc/prometheus/prometheus.yml.bak
sudo sed -i 's/^      - targets/\t  - targets/' /etc/prometheus/prometheus.yml
promtool check config /etc/prometheus/prometheus.yml
curl -s -X POST http://localhost:9090/-/reload; echo

You’ll get this error:

Checking /etc/prometheus/prometheus.yml
  FAILED: parsing YAML file /etc/prometheus/prometheus.yml: yaml: line 11: found character that cannot start any token

failed to reload config: couldn't load configuration (--config.file="/etc/prometheus/prometheus.yml"): parsing YAML file /etc/prometheus/prometheus.yml: yaml: line 11: found character that cannot start any token

YAML allows only spaces for indentation, so a tab at the start of line 11 can’t begin any token. The reload failed but the server kept running with its old config, and prometheus_config_last_reload_successful is now 0. If you had restarted instead of reloading, the service would have died with the same message in the journal and stayed down.

Fix: find the tab, then restore or retype the line with spaces:

grep -nP '\t' /etc/prometheus/prometheus.yml
sudo cp /etc/prometheus/prometheus.yml.bak /etc/prometheus/prometheus.yml
promtool check config /etc/prometheus/prometheus.yml && sudo systemctl reload prometheus
11:	  - targets: ["localhost:9090"]

In vim, :set list shows tabs as ^I, and adding set expandtab to ~/.vimrc stops vim from typing them.

✅ Checkpoint: promtool says SUCCESS again and prometheus_config_last_reload_successful is back to 1.

2The data folder belongs to the wrong user

This happens when someone runs the binary by hand with sudo against the real data folder, or restores a backup as root. Simulate it:

Run this on prom01:

sudo systemctl stop prometheus
sudo chown -R root:root /var/lib/prometheus
sudo systemctl start prometheus
sleep 3; systemctl is-active prometheus
sudo journalctl -u prometheus -n 20 --no-pager | grep level=ERROR

You’ll get this error:

activating
Nov 08 05:41:17 prom01 prometheus[1904]: time=2026-11-08T05:41:17.632Z level=ERROR source=query_logger.go:138 msg="Error opening query log file" component=activeQueryTracker file=/var/lib/prometheus/queries.active err="open /var/lib/prometheus/queries.active: permission denied"
Nov 08 05:41:17 prom01 prometheus[1904]: time=2026-11-08T05:41:17.632Z level=ERROR source=main.go:1027 msg="failed to initialize active query tracker" err="create mmap-ed active query log: open /var/lib/prometheus/queries.active: permission denied"

The first file Prometheus opens in the data folder is queries.active, its record of running queries, and the prometheus user can’t write to a root-owned file. systemd keeps restarting it every 5 seconds and it keeps failing, which is why is-active says activating instead of active. If only the wal folder were wrong you’d see opening storage failed: open /var/lib/prometheus/wal/00000000: permission denied instead.

A close cousin is the lock error. If a second copy (a hand-started test, or two units) points at the same folder, it stops with opening storage failed: lock DB directory: resource temporarily unavailable. Here the fix is to stop the extra copy, never to delete the lock file.

Fix:

sudo chown -R prometheus:prometheus /var/lib/prometheus
sudo systemctl restart prometheus
systemctl is-active prometheus
active

✅ Checkpoint: Prometheus is active again and ls -l /var/lib/prometheus shows prometheus as owner of every file.

3Port 9090 is already taken

A forgotten copy from Lesson 1 (left running in tmux or screen) is the classic cause. Start one by hand while the service is stopped, then try the service:

Run this on prom01:

sudo systemctl stop prometheus
cd ~/dl/prometheus-3.16.0.linux-amd64 && nohup ./prometheus --config.file=prometheus.yml --storage.tsdb.path=/tmp/promtest >/dev/null 2>&1 &
sudo systemctl start prometheus; sleep 3
sudo journalctl -u prometheus -n 20 --no-pager | grep level=ERROR

You’ll get this error:

Nov 08 05:46:09 prom01 prometheus[2011]: time=2026-11-08T05:46:09.245Z level=ERROR source=main.go:1280 msg="Unable to start web listener" err="listen tcp 0.0.0.0:9090: bind: address already in use"

Only one process can listen on a TCP port. The service starts, fails to bind, exits with status 1 and goes into the restart loop, while the stray copy happily answers on 9090, so the UI still works and it’s easy to miss that you’re looking at the wrong server.

Fix: find the owner of the port and stop it:

sudo ss -ltnp | grep 9090
kill 2003                      # use the pid that ss shows for your stray copy
rm -rf /tmp/promtest
sudo systemctl restart prometheus && systemctl is-active prometheus
LISTEN 0      4096         0.0.0.0:9090       0.0.0.0:*    users:(("prometheus",pid=2003,fd=8))
active

A quick way to tell the two apart is the CGroup line in systemctl status: the service’s PID lives under /system.slice/prometheus.service, a stray copy doesn’t.

✅ Checkpoint: ss shows one prometheus on 9090 and its PID matches the Main PID in systemctl status.

This happened at work

The Monday morning with no graphs

My team lead in Chennai likes to tell this one. She was supporting the back office of a bank there, where Prometheus watched the servers that ran the overnight reconciliation batches. Patching happened on Sunday nights, and one Monday at 8:30 the operations manager called to ask why the batch dashboard had been blank since 1 am.

The first guess was the batch servers, so two engineers spent twenty minutes checking them, and they were fine. Then someone noticed that Grafana’s data source test was failing, and systemctl status prometheus showed the service flapping in “activating (auto-restart)”. The journal said permission denied on queries.active.

The story behind it came out over tea. On Friday an engineer had wanted to try a flag, stopped the service and started the binary by hand with sudo against /var/lib/prometheus. It ran happily as root for two days and created new block folders owned by root.

When the server rebooted after patching, systemd started Prometheus as the prometheus user, which could no longer write its own folder.

The fix took one chown -R. The changes that stuck were bigger: nobody runs the real binary by hand any more (tests use a copy with a separate data path and port), a second small Prometheus in another data centre now scrapes the first one and alerts if up goes to 0, and the patch checklist ends with systemctl is-active prometheus.

Common mistakes and troubleshooting

MistakeWhat happensWhat to do instead
Restarting instead of reloading after a config editA typo kills the service and you lose monitoring until you noticeRun promtool check config, then reload with systemctl reload or POST /-/reload
Running the binary by hand with sudo on the real data folderNew files are owned by root and the service fails on the next startTest with a copy, its own storage.tsdb.path and another port
Installing the Ubuntu archive package and following upstream docsPaths, flags and versions don’t match what you readUse the official tarball and the layout in this lesson, or follow the package’s own docs
Skipping the checksumA cut-off or tampered download fails in odd ways laterAlways run sha256sum -c with sha256sums.txt
Forgetting daemon-reload after editing the unitsystemd keeps using the old flags and your change seems ignoredRun sudo systemctl daemon-reload, then restart
Deleting the lock file to “fix” a lock errorTwo servers write the same TSDB and corrupt itFind and stop the second copy with ss -ltnp or ps
Upgrading without a snapshot or release notesA breaking change leaves you with no quick way backSnapshot, read the notes, check the config with the new promtool, keep the old binary

Interview questions

Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.

Fresher level

1. How would you check that a Prometheus config change is safe before applying it?

Why they ask this: They want to hear promtool and reload, the basic daily habit.

How you might answer: I run promtool check config on the file; it parses the YAML and the rule files the same way the server does and exits 0 only if everything is valid. Then I reload with sudo systemctl reload prometheus or a POST to /-/reload, and check prometheus_config_last_reload_successful is still 1.

In a team I put the same promtool check in the CI pipeline, so a broken file can’t be merged.

2. Where does Prometheus keep its data and logs on a systemd install?

Why they ask this: A simple check that you know the layout and where to look first.

How you might answer: Data goes to the folder given by storage.tsdb.path, in our setup /var/lib/prometheus, with the WAL, the head chunks and the two-hour blocks. Prometheus logs to stderr, so systemd puts it in the journal and I read it with journalctl -u prometheus.

2–5 years experience

3. Prometheus won’t start after a server reboot. Walk me through how you find the cause.

Why they ask this: They want a calm, ordered approach, starting with the evidence instead of guesses.

How you might answer: I start with systemctl status prometheus to see whether it’s crashing or not enabled, then journalctl -u prometheus filtered on level=ERROR for the real message. The usual ones are a YAML parse error, permission denied on the data folder, a lock error from a second copy, or bind: address already in use.

Each has a direct check: promtool check config, ls -l on /var/lib/prometheus, ps for another prometheus, and ss -ltnp for port 9090.

4. What is the difference between /-/healthy and /-/ready, and what does a reload do inside the process?

Why they ask this: They are checking whether you understand the server lifecycle, which matters for probes and automation.

How you might answer: /-/healthy returns 200 once the process is up; /-/ready returns 200 only after the TSDB has replayed the WAL and the server can answer queries. I’d use healthy for a liveness probe and ready for a readiness probe or before sending traffic.

A reload re-reads the config and rule files inside the same process, so scrape jobs and rules change without losing in-memory data. If the new file is bad, the old config stays active and the reload metric goes to 0.

Senior level

5. Your bank asks whether to run Prometheus latest or LTS. What do you recommend and how do you plan upgrades?

Why they ask this: They want judgement balancing risk, features and the organisation’s approval process.

How you might answer: For a bank with strict change approval I’d run the LTS line, 3.13 right now, because it gets fixes for about a year and fewer feature changes to test. If a team needs a newer feature, they can run latest on a non-critical server first.

Either way upgrades follow one runbook: read the release notes, check the live config with the new promtool, snapshot or back up the data folder, swap the binaries, start, and check /-/ready, the version and Target health. The old binary stays on disk until the new one has run cleanly for a while.

6. How would you harden this systemd unit further?

Why they ask this: They’re looking for practical Linux security knowledge beyond “it works”.

How you might answer: It already runs as a nologin system user. I’d add systemd sandboxing such as ProtectSystem=strict with ReadWritePaths=/var/lib/prometheus, ProtectHome=true, NoNewPrivileges=true and PrivateTmp=true, then test that reloads, rules and the data folder still work.

I’d also stop binding 9090 to every interface and put it behind a reverse proxy with TLS and authentication, which is the next lesson.

Quick quiz

Q1. Which command checks prometheus.yml for errors without touching the running server?

  1. prometheus check
  2. promtool check config /etc/prometheus/prometheus.yml
  3. systemctl verify prometheus
  4. yamllint prometheus.yml
Show answer

B — promtool check config parses the file and its rule files the same way the server does.

Q2. What does curl -X POST http://localhost:9090/-/reload need to work?

  1. Root access
  2. The web.enable-lifecycle flag
  3. The admin API flag
  4. A restart first
Show answer

B — Without the web.enable-lifecycle flag the server answers “Lifecycle API is not enabled.”

Q3. You reload with a broken prometheus.yml. What happens?

  1. Prometheus crashes
  2. Prometheus deletes the bad file
  3. The reload fails and the old config keeps running
  4. All targets go down
Show answer

C — A failed reload keeps the previous config and sets prometheus_config_last_reload_successful to 0.

Q4. The service fails with “open /var/lib/prometheus/queries.active: permission denied”. What is the likely fix?

  1. Increase LimitNOFILE
  2. chown -R prometheus:prometheus /var/lib/prometheus
  3. Delete the WAL folder
  4. Change the port
Show answer

B — The data folder has the wrong owner, so the prometheus user can’t write to it.

Q5. What does systemctl enable with the now option do for prometheus?

  1. Starts it once
  2. Enables it at boot only
  3. Enables it at boot and starts it now
  4. Reloads its config
Show answer

C — The now option combines enable (start at boot) with an immediate start.

Quick recap

  • Download the official tarball and verify it with sha256sum -c against sha256sums.txt.
  • Binaries live in /usr/local/bin, config in /etc/prometheus, data in /var/lib/prometheus, and the service runs as the nologin user prometheus.
  • The systemd unit sets config path, data path, 15d retention and the lifecycle API, and ExecReload sends SIGHUP.
  • Always run promtool check config before a reload; a bad reload keeps the old config, a bad restart stops monitoring.
  • Upgrades are a binary swap with a snapshot and the new promtool check first; pick LTS (3.13) or latest (3.16) to suit how often you can change.

Next lesson

Lesson 3: Secure Prometheus with Nginx, HTTPS and basic auth → Right now anyone on the network can open port 9090. Next you put Prometheus behind Nginx with HTTPS and a password, and lock the port down.

Further reading: install Prometheus on Ubuntu

Want to go deeper into install Prometheus on Ubuntu? The official installation page, configuration reference and command-line flags list cover every option used here, and the GitHub releases page has the notes to read before each upgrade. If systemd and journalctl are new to you, the Linux administration course explains services and logs from the start, and Lesson 1 has the lab setup.

You can also see the full plan for this course on the Prometheus Monitoring course page.

Scroll to Top

This website uses cookies

We use a few cookies to keep this site working. If you allow it, we also use Google Analytics to count visits so we can improve our lessons. You can accept all, deny the optional ones, or choose what to allow.