Skip to main content
RunBook Academy

LinuxLXXIII · Change ManagementExecution

Pre-checks and state capture - what to record before you touch anything

Intermediate⏱ ~15 min🧪 Lab requiredbashsystemctljournalctldfss

What you'll learn

  • Prove a host is in a known-good state before changing it
  • Capture a before picture that makes post-change validation a comparison rather than a judgement
  • Detect pre-existing drift that would invalidate both the review and the rollback
  • Confirm the recovery path works before you need it

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11

Not yet marked complete on this device.

Half the incidents attributed to a change were not caused by it. The host had a failed unit before anyone logged in, or a filesystem at 98%, or a service that had been quietly restarting every ten minutes since Tuesday. The change arrived, the symptom became visible, and the change got the blame - and the rollback, which of course did not help.

Pre-checks exist to answer one question before you start: was this host healthy a minute ago? If you cannot answer it, you cannot interpret anything that happens next.

Two jobs, one pass

The pre-check pass does two different things and it is worth being clear which is which.

  • Gating: proving the host is in a state where the change is safe to attempt. A gate can fail, and a failed gate stops the change.
  • Capture: recording the before picture, so that afterwards you can compare rather than judge. Capture cannot fail; it just has to happen before you disturb anything.

The change management checklist in this course lists the gates as items to confirm. This lesson is about running them and keeping the output, because a checklist tick is an assertion while a command’s output is evidence with a timestamp.

The gates

Read-only / Safegate 1: the host says it is healthy
$ systemctl is-system-running; systemctl list-units --state=failed --no-legend
degraded
● chrony.service loaded failed failed chrony, an NTP client/server

Illustrative output

degraded is the most valuable single word in a pre-check. It means something was already broken, and it means that any symptom you see after the change has at least two candidate explanations.

Read-only / Safegate 2: room to perform the change
$ df -h --output=pcent,ipcent,avail,target -x tmpfs -x devtmpfs -x squashfs
Use% IUse%  Avail Mounted on
47%    4%    62G /
91%   12%   3.2G /var

Illustrative output

Read-only / Safegate 3: nothing else is about to run
$ journalctl -p err -b --since '24 hours ago' --no-pager | wc -l; systemctl list-timers --all --no-pager | head -5
3
NEXT                        LEFT      LAST                        PASSED    UNIT
Tue 2026-08-11 22:00:00 UTC 6h left   Mon 2026-08-10 22:00:00 UTC 18h ago   backup.timer
Tue 2026-08-11 21:15:00 UTC 5h left   Tue 2026-08-11 09:15:00 UTC 6h ago    apt-daily.timer
Wed 2026-08-12 06:00:00 UTC 14h left  Tue 2026-08-11 06:00:00 UTC 9h ago    logrotate.timer

Illustrative output

The remaining gates are less about the host and more about the recovery path.

  • Out-of-band console answers, tested today - not assumed from the inventory
  • The rollback artefact exists and is fetchable: the previous package, image, or snapshot
  • Monitoring is up and not silenced for these hosts, so a regression is visible
  • The configuration under change is in version control with no uncommitted local edits
  • A second person knows the change is starting and when it is expected to end

Drift is a gate, not a detail

A change reviewed against the repository is a change reviewed against a host that may not exist. If somebody fixed something by hand at 03:00 last quarter and never committed it, your change is being applied to a configuration nobody has read, and your rollback will restore the committed state - silently discarding their fix.

Read-only / Safegate 4: no uncommitted drift
$ sudo git -C /etc status --short; sudo git -C /etc log -1 --format='%h %cd %s'
 M nginx/conf.d/upstream.conf
M sysctl.d/99-tuning.conf
a41f0c2 Tue Jul 14 09:12:44 2026 +0000 base role apply

Illustrative output

Where etckeeper is not installed, the package manager can still tell you which packaged files have been modified:

# Debian family - requires the debsums package
sudo debsums -ce

# RHEL family - built in
rpm -Va --nofiles --noscripts 2>/dev/null | head

Both list configuration files whose contents no longer match the package. That list is your drift inventory on a host with no configuration management history.

The capture

Capture is cheap, read-only, and the difference between “it looks fine” and “it matches”. Write it into one timestamped directory so the whole set can be handed to somebody else.

Read-only / Safethe before picture
$ CHG=/var/tmp/chg-$(date -u +%Y%m%dT%H%M%SZ); mkdir -p "$CHG"; systemctl list-units --state=failed --no-legend > "$CHG/failed.txt"; systemctl list-unit-files --state=enabled --no-legend > "$CHG/enabled.txt"; dpkg -l > "$CHG/packages.txt" 2>/dev/null || rpm -qa | sort > "$CHG/packages.txt"; ss -tlnp > "$CHG/listeners.txt"; ip -br addr > "$CHG/addr.txt"; ip route > "$CHG/route.txt"; df -h > "$CHG/df.txt"; sysctl -a > "$CHG/sysctl.txt" 2>/dev/null; echo "$CHG"
/var/tmp/chg-20260811T154207Z

Illustrative output

The single highest-value item in that set is the listener list. After the change, one diff answers the question that actually matters:

Read-only / Safewhat actually changed
$ ss -tlnp > /var/tmp/after-listeners.txt; diff "$CHG/listeners.txt" /var/tmp/after-listeners.txt
5c5
< LISTEN 0  511  0.0.0.0:443  0.0.0.0:*  users:(("nginx",pid=1204,fd=7))
---
> LISTEN 0  511    127.0.0.1:443  0.0.0.0:*  users:(("nginx",pid=8891,fd=7))

Illustrative output

Make the capture part of the procedure

A pre-check performed only when the operator remembers is a pre-check performed on the low-risk changes and skipped on the tense ones. Put it in the runbook as step zero, keep its output next to the change record, and gate the change on its exit status where you can:

set -e
systemctl is-system-running >/dev/null    # non-zero unless "running"
test "$(systemctl list-units --state=failed --no-legend | wc -l)" -eq 0
test "$(df --output=pcent /var | tail -1 | tr -dc '0-9')" -lt 85
test -z "$(git -C /etc status --porcelain)"
echo "pre-checks passed"

Anything that exits non-zero stops the change before it starts, which is the cheapest place for a change to stop.

Knowledge check

Knowledge check · 6 questions

  1. Q1. systemctl is-system-running reports degraded before you begin a change. What is the correct response?

  2. Q2. Why is /var at 91% a gate failure for a package upgrade rather than a warning?

  3. Q3. git -C /etc status --short returns two modified files before your change. What is the risk if you proceed?

  4. Q4. After a change, nginx is active, the unit is running, and curl to localhost returns 200 - but external clients are refused. Which pre-change capture would have caught this immediately?

  5. Q5. Which of these belong in a pre-change capture rather than in a pre-change gate? Select all that apply.

  6. Q6. A systemd timer scheduled to fire inside the change window is a scheduling detail rather than a pre-check failure.

Passing score: 75%. Answers are checked in this browser.