Skip to main content
RunBook Academy

VyOSVI · Commit and Rollback SafetySafe changes

commit semantics — what the commit engine actually does

Intermediate⏱ ~18 mincommitcommit-confirmcompareshow system commitshow log

What you'll learn

  • Walk a commit from candidate to running and name what runs at each step
  • Distinguish a set-time constraint failure, a verify() failure, and a backend rejection
  • Use show system commit and its diff to find what a change actually did
  • Recognise the commit failure modes that leave a router partly configured

Prerequisites

Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling)

Not yet marked complete on this device.

commit semantics — what the commit engine actually does

The commit command takes the candidate configuration and turns it into runtime state. Almost every operator carries a mental model of this that is slightly wrong, and the wrong part is always the same: they believe a failed commit changes nothing. It is a comfortable belief and it is why half-configured routers surprise people.

This lesson walks what actually runs, in what order, and what is left behind when one step of it fails.

Where the checks actually happen

The first thing to unlearn is that commit is where your configuration gets checked. Some of it is checked much earlier, and knowing which is which is most of the diagnostic skill.

flowchart TB
  A["set / delete<br/>(you type it)"] --> B{"Value constraint<br/>from the interface definition"}
  B -->|fails| B1["rejected here<br/>nothing enters the candidate"]
  B -->|passes| C["candidate configuration"]
  C --> D["commit"]
  D --> E["priority-ordered conf_mode scripts"]
  E --> F["get_config()"]
  F --> G["verify()"]
  G -->|ConfigError| G1["this script fails<br/>commit reports failure"]
  G -->|ok| H["generate()<br/>render FRR / nftables / systemd files"]
  H --> I["apply()<br/>reload the backend"]
  I --> J["next script, same four steps"]
  J --> K["archive revision"]

Where commits fail, and how the message tells you

WhereWhat it catchesWhat it looks like
set-time constraintFormat, range, enumerationrejected as you type; nothing enters the candidate
verify()Cross-node references and required combinationsa hand-written sentence naming the node
generate() / apply()The backend refusing what VyOS rendereda template or daemon error, often after other scripts already applied

The message shape tells you which one you are in, and each one implies a different next move. A set-time rejection needs a different value. A verify() failure needs a different combination — usually a node you have not created yet. A failure in apply() means the box is already partly changed and the question is no longer “what did I type wrong” but “what state is this router in right now”.

“All or nothing” is not what a commit promises

Because the engine runs one script after another, a commit that fails in the middle has already run the scripts before it. The interfaces script applied; the BGP script then raised ConfigError and stopped. Nothing rolls the interfaces back.

That is the honest model, and it is the one to keep in your head at 03:00: a failed commit does not restore the router to the state it was in before you typed it. It stops. Where it stopped depends on priority order, which is why the same typo produces a harmless failure in one subtree and a half-configured router in another.

The candidate configuration, on the other hand, is untouched by a failure — your changes are all still there in the session, which is what lets you fix the one bad node and commit again rather than retyping. The confusion the phrase “atomic commit” causes is exactly this: the session is safe, the box may not be.

The revision archive

Every successful commit archives the configuration. The revisions live in /config/archive/ as gzipped files, and how many are kept is a configured number:

set system config-management commit-revisions '200'
set system config-management commit-archive location 'scp://user:pass@backup.example.net/vyos'

The second line is the one that survives losing the router: on every successful commit the configuration is also copied to a remote location over SCP, SFTP, FTP or TFTP, named after the host and the timestamp. A router whose disk you cannot read is not a router whose configuration you have lost. Note that the credentials live in the URI, so this node is one of the few places a password ends up in the configuration — which is an argument for a dedicated, write-only account on the archive host.

Read the history with show system commit:

Read-only / Safecommit revisions
vyos@vyos:~$ show system commit
0   2026-08-15 14:01:12 by vyos via cli
1   2026-08-15 11:42:03 by vyos via cli
2   2026-08-14 14:23:00 by ansible via cli
3   2026-08-14 09:15:41 by vyos via boot-config-loader

Illustrative output

Revision 0 is the current configuration, so “the change before this one” is revision 1. Two commands turn that list into evidence:

show system commit diff 1
show system commit file 1

diff shows what changed relative to the running configuration — the fastest way to answer “what did the last change actually do”. file prints the whole configuration as it was at that revision, which is what you want when the question is “what did this router look like on Thursday”.

How it fails

The production failure modes the engineer must recognise:

  • A value the CLI rejects as you type it. Format, range or enumeration. It never reaches the candidate, and no commit is involved. Fix the value.
  • A verify() failure naming a node that does not exist. The classic is a reference: a neighbour pointing at a peer-group that was never created, a rule referencing an address-group that is not defined, an interface named in a policy that is not configured. The message names both ends. Create the missing node in the same session and commit once.
  • A backend that refuses what VyOS rendered. The VyOS syntax was fine, and FRR or nftables rejected the result. The commit reports a failure from the apply() of that one script, and the scripts before it have already run.
  • Commits from two sessions at once. VyOS serialises them — the second operator’s commit waits rather than interleaving. What this looks like from the outside is a commit that hangs, and the correct response is to find the other session, not to interrupt yours.
  • A commit that succeeds and breaks the service anyway. No layer of the engine has an opinion about whether your routing policy was a good idea. This is the failure mode commit-confirm exists for.

Rollback

  • rollback 1 — go back one revision. On VyOS this reboots the router to load the older configuration, so it is a maintenance action, not a quick undo; plan for the outage.
  • compare 1 — before rolling back, see exactly what rolling back would change.
  • load an archived file then commit — list /config/archive/ first and load the gzipped revision you want. This avoids the reboot, at the cost of re-running every script, so it can fail in the middle exactly like any other commit.
  • discard — throw away an uncommitted candidate. Nothing on the box changes, because nothing had been applied.

Production discipline

Cross-course references

The Linux course’s V-Linux-NetConfig covers the underlying netlink semantics. The OPNsense course’s XXXVIII-OPNsense-Troubleshoot covers the equivalent failure modes on the firewall side. The Ansible course’s XLII-Ansible-BeyondLinux covers how to drive commits from Ansible with the same review workflow.

Quiz

Knowledge check · 4 questions

  1. Q1. An operator types `set protocols bgp system-as 4294967296` and the CLI rejects it immediately, without a commit. Which check rejected it?

  2. Q2. A commit that fails leaves the router in the state it was in before the commit was typed.

  3. Q3. A commit fails with `Specified peer-group "UPSTREAM" for neighbor "192.0.2.2" does not exist!`. What kind of failure is this, and what is the correct fix?

    The operator is adding a peer to an existing BGP configuration and typed `set protocols bgp neighbor 192.0.2.2 peer-group UPSTREAM`. Every individual value was accepted as it was typed. The commit is refused.

  4. Q4. A commit reported success, and BGP sessions dropped seconds later. Where does the operator look, and what does this say about the engine?

    A change set touching several subtrees was committed during a maintenance window. VyOS reported no error. Within a minute, two eBGP sessions went from Established to Active.

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