Skip to content

Policy rollback

Every successful request policy reload takes a snapshot of /etc/enforcegate/rules.d/ before applying the new rule set. Snapshots live under [policy].backup_path (default /var/lib/enforcegate/policy-backups/) as timestamped subdirectories mirroring the rules.d/ layout. The engine retains the N most-recent snapshots — bounded by [policy].backup_depth (default 10) — and prunes older ones after every successful reload.

This page covers the operator path to undo a policy load. For the optional per-commit audit trail and per-rule blame that sit on top of the snapshot system, see policy audit and history.

When to roll back

  • A request policy reload succeeded but the live engine is matching URLs against rules you didn't intend (over-broad regex, wrong action, copy-paste of the wrong policy file).
  • An automated pipeline applied a .policy change that turned out to be wrong.
  • An audit trail asks "what was the policy on date X?" — listing snapshots gives a quick answer.

A rollback is not the right tool for fixing engine internals, license issues, or non-policy bugs. It only swaps the .policy files in rules.d/ and re-runs the compile.

Step 1 — List available snapshots

eghost cli
> show policy backups
Gen  Snapshot                      Files   Note
1    20260528-1503.001             12      (currently applied)
2    20260528-1320.001             11
3    20260527-0945.001             10
4    20260526-1842.001             10

Generation 1 is whatever was applied by the most recent request policy reload. Generation 2 is the snapshot taken immediately before — i.e. the state that was current before generation 1 became current. Higher numbers are older.

Available at Monitoring privilege and above.

Step 2 — Preview the rollback

Always preview before applying. The dry-run reports which files would be removed (orphans added after the snapshot was taken) and which would be overwritten (operator-edited since the snapshot):

> request policy rollback 2 dry-run true
Rollback dry-run from gen 1 → gen 2:
  - would remove orphan file: 75-temp-experiment.policy
  - would overwrite operator-edited file: 50-deny-shorteners.policy

This rollback would discard 1 file you added since the snapshot and overwrite
1 file you edited. Re-run without dry-run to apply, or pass yes true to skip
the interactive confirmation.

The diff is your "are you sure?" gate. If the dry-run reports a clean delta (no orphans, no overwritten operator edits) the rollback is safe to apply.

Scripted equivalent (nicli)
docker exec enforcegate egctl request-policy-rollback 2 --dry-run

Step 3 — Apply

Interactive — the client re-renders the diff and prompts for confirmation:

> request policy rollback 2

To skip the interactive confirmation (CI pipelines, automation):

> request policy rollback 2 yes true

The engine refuses to roll back over operator-added files unless yes true (or the nicli --yes flag) is present — the gate is intentional so a careless rollback doesn't wipe new policy work.

On success:

  • rules.d/ is replaced with the contents of snapshot generation 2.
  • A new snapshot (the pre-rollback state, i.e. generation 1) is recorded as the new generation 2 — so the rollback itself is also undoable.
  • The compiled policy in engine.db is regenerated from the rolled-back files.
  • The live in-memory policy graph is reloaded.
Scripted equivalent (nicli)
docker exec enforcegate egctl request-policy-rollback 2 --yes

Step 4 — Verify

Confirm the rollback applied:

> show policy backups
Gen  Snapshot                      Files   Note
1    20260528-1503.002             11      (currently applied)
2    20260528-1503.001             12
3    20260528-1320.001             11

> show policy match https://some-test-url.example/
URI:        https://some-test-url.example/
Matched:    no (permit-by-default)
Code:       200 (permit)

The freshly-applied state appears as the new generation 1; the state that was current before the rollback drops to generation 2.

Retention

[policy].backup_depth (default 10) sets how many snapshots are kept. After every successful reload or rollback, snapshots beyond the depth are pruned. Older snapshots cannot be recovered after pruning — they're gone from disk.

To keep a longer history, bump the depth in engine.conf:

[policy]
backup_depth = 30

Restart or reload the engine to pick up the change.

Limitations

  • Snapshots cover rules.d/ only. They do not include engine.conf schema changes (e.g. flipping a default-permit to default-deny via [policy] settings), license material, or operator config edits. Roll those back separately.
  • The snapshot directory is on the persistent enforcegate-config volume. A docker compose down -v (factory reset) wipes the volume — and with it, all snapshots. Back up the volume before any reset.
  • Snapshots are taken pre-apply. If the engine crashes during request policy reload after the snapshot is taken but before the in-memory graph is replaced, the on-disk engine.db may end up between states. The engine's boot-time rescue rebuilds engine.db from rules.d/ if it detects an inconsistent state — but in practice, just re-run request policy reload after the restart.