What changed between 2.x and 3.x¶
The Backup and Restore module was substantially reworked for GeoServer 3.x. The REST API, the
archive layout and the on-disk format remain compatible, so existing automation and existing
archives keep working. Most of the changes are reliability fixes and new options; there is one
default behaviour change — BK_PRESERVE_IDS — that you should be aware of before upgrading
scripted backups.
At a glance¶
| Area | 2.x | 3.x |
|---|---|---|
| Batch engine | Spring Batch 4 / Spring 5, jobs wired in XML | Spring Batch 6 / Spring 7, jobs wired in Java |
| Default archive | name-based (ids regenerated on restore) | id-preserving (BK_PRESERVE_IDS=true by default) |
| Filtered (subset) backups | could be incomplete (dangling references) | self-contained via dependency closure |
| Merge / partial restore + GWC | wiped the target's tile layers | keeps the target's tile layers |
| Restore validation | could false-fail name-based restores | null-id guards + trustworthy pre-flight pass |
| Security restore | verbatim copy, single-instance only | merge mode + keystore re-encryption for cross-instance migration |
| Dry-run / fail-on-invalid | wrote to disk incrementally (only the reload was skipped) | snapshot + rollback leave the data directory untouched |
BK_PRESERVE_IDS now defaults to true — the one to watch¶
This is the only change that alters the default outcome of a backup.
In 3.x a backup keeps each catalog object's internal id and writes cross-references by id, so the archive is a portable migration artifact: restoring it into another, already-populated GeoServer preserves the original identities, which lets cross-references and GeoWebCache tile-layer links re-link correctly, and lets a re-restore into the same instance skip objects that already exist (by id) instead of duplicating or clashing on name.
If you rely on the classic name-based archive — ids stripped and regenerated on restore, which is the right choice when restoring into a fresh, empty data directory — set the option explicitly:
Note
BK_PRESERVE_IDS is a backup-side option only. The restore adapts automatically to whatever
the archive contains (it reads whichever of <id>/<name> each reference carries), so no matching
option is required on the restore command. See
Migrating a catalog to another GeoServer instance.
Filtered (subset) backups are now self-contained¶
A workspace-filtered backup in 2.x captured only the objects directly inside the selected workspaces. If one of those layers used a global (workspace-less) style, or a layer group referenced members in another workspace, the archive carried dangling references and could fail to restore into an empty target.
In 3.x a filtered backup computes a dependency closure before writing: global styles, cross-workspace layer-group members and the namespaces they need are pulled into the archive automatically. A subset backup is therefore self-contained and restores cleanly into an empty instance.
GeoWebCache tile layers survive a merge restore¶
In 2.x a restore wiped the target's gwc-layers directory whenever the restore was filtered,
destroying tile-layer configuration for layers that had nothing to do with the archive.
In 3.x the GeoWebCache directory is wiped only on a full purge restore
(BK_PURGE_RESOURCES=true with no workspace filter). A merge restore
(BK_PURGE_RESOURCES=false) or a filtered restore keeps the target's existing tile layers and
merges the archive's tile layers on top. This makes incremental, per-workspace restores safe on a
server that already serves tiles.
Stronger, trustworthy restore validation¶
Two fixes make restore validation both less noisy and more dependable:
- No more false failures on name-based restores (GEOS-10877). A name-based archive carries objects with no ids; in 2.x feeding those to the catalog validator could raise spurious errors. In 3.x per-item validation is skipped for null-id objects, so a name-based restore no longer false-fails.
- A pre-flight validation pass. After the restore catalog has been fully assembled, a dedicated
step validates the whole catalog and reports any problems. By default it logs them and records
them as execution warnings. Set
BK_FAIL_ON_INVALID=trueto make the restore abort on the first invalid object — the job is markedFAILED, the live configuration reload is skipped, and the data directory is rolled back (see Transactional Dry-Run and fail-on-invalid below). Combine it withBK_DRY_RUN=trueto validate an archive non-destructively before committing.
Migration-safe security restore¶
Restoring security across two different instances used to be impossible: 2.x copied the security
folder verbatim, so a keystore encrypted with the source's master password could not be read on a
target with a different one, and the copy overwrote the target's whole security configuration.
3.x adds the pieces needed for cross-instance security migration:
BK_MERGE_SECURITY— an add-only merge of the archive's users, groups and roles into the target's existing security services. The target keeps its own configuration, keystore and master password; only principals that are not already present (by name) are added. Works even when the source and target master passwords differ.- Automatic service bootstrap — if the target is missing a user/group or role service that the merge needs, an empty one is created for it.
- Keystore re-encryption — on a security replace restore, supply
BK_SOURCE_MASTER_PASSWORDandBK_TARGET_MASTER_PASSWORDtogether and the archive's keystore is re-encrypted from the source master password to the target's, so it becomes readable on the target.
See BK_MERGE_SECURITY and the master-password options for the REST parameters.
Warning
Security is still excluded by default (BK_SKIP_SECURITY=true). The options above only take
effect when you opt security in with BK_SKIP_SECURITY=false.
Transactional Dry-Run and fail-on-invalid¶
A restore commits to the data directory incrementally as its steps run — the restore catalog
persists each item through GeoServerConfigPersister, and GeoWebCache and security are written by
their tasklets. In earlier versions a Dry-Run therefore only skipped the final in-memory reload:
it still wrote the catalog to disk along the way, so it was not truly non-destructive, and a
BK_FAIL_ON_INVALID abort could leave a partial write behind.
3.x makes both opt-in modes trustworthy on disk by snapshotting and rolling back at the job level:
- Before the job, when the restore is a Dry-Run (
BK_DRY_RUN=true) or opts intoBK_FAIL_ON_INVALID=true, the affected data-directory subtrees (workspaces,styles,layergroups,gwc,gwc-layers,security) and the root global*.xmlfiles are copied into a temporary snapshot. This is strictly opt-in, so an ordinary restore keeps the historical incremental-commit behaviour and pays no snapshot cost. - After the job, if the restore was a Dry-Run or ended in any non-
COMPLETEDstate, the live tree is rolled back from the snapshot (subtrees the restore created are removed; modified or deleted ones are restored) and the configuration and security are reloaded. The snapshot is then deleted; if a rollback ever fails it is preserved and logged for manual recovery.
The practical effect: a BK_DRY_RUN=true restore now leaves the data directory exactly as it was —
making it a safe way to validate an archive — and a failed or BK_FAIL_ON_INVALID-aborted restore no
longer leaves the target half-written.
REST API and archive compatibility¶
The REST endpoints (/rest/br/backup, /rest/br/restore) and the JSON/XML response shape are
unchanged from 2.x, so existing clients and scripts continue to work without modification. Archives
written by 3.x restore into 3.x; the practical difference from a 2.x archive is the id-preserving
default described above.
For developers and extension authors¶
- The engine now runs on Spring Batch 6.0.3 / Spring 7 (GeoServer 3's JDK 17 baseline applies).
- The backup and restore job graphs are defined in Java
@Configuration(BackupJobConfiguration,RestoreJobConfiguration,BatchInfrastructureConfiguration) instead of the oldapplicationContext.xml<batch:job>definitions. Custom steps, tasklets, readers, processors and writers contributed by extensions should be wired the same way.