Chapter 27Lesson 05260–360 min

Checkpoint Lab — Upgrade Planning, Version Support, Breaking Changes, Java Runtime Upgrades, and Rollback Strategy

The checkpoint lab treats an upgrade as an auditable change: define service objectives, freeze the exact source evidence, prove a recovery point, calculate version gates, rehearse, inject a failure, validate the corrected target, make a go/no-go decision, and demonstrate rollback without touching production infrastructure.

CheckpointRunbookFailure injectionGo/no-goRollback

Learning objectives

  • Build a complete upgrade runbook and evidence packet.
  • Predict state changes before target startup.
  • Block an upgrade on an injected compatibility failure and repair it safely.
  • Validate repository bytes, permissions, tasks, runtime, and client behavior.
  • Demonstrate rollback by restoring the untouched checkpoint rather than downgrading upgraded state.
Dated baseline (27 August 2026). Lessons use Nexus Repository 3.95.2-01 as the current reference line and Java 21 as the required runtime family. Always re-check the live version-status and release-note pages before a real change window.
Upgrade is not migration. Replacing Nexus application binaries, migrating a database engine, moving blob storage, and redesigning topology are different changes. Chapter 27 upgrades a supported instance; do not combine unrelated migrations unless the documented path requires them.
No blind downgrade. If a new Nexus version has changed database/data state, do not start older binaries against the upgraded data directory. Rollback means restoring a verified pre-upgrade recovery checkpoint or following an explicit Sonatype-supported procedure.

1. Scenario and objectives

You operate a disposable single-node Community-style instance modeled as Nexus 3.84.2/H2/file blob. The desired target is the 3.95.2-01 line with Java 21. RTO for the drill is 45 minutes; no target-only publications are allowed until acceptance. You must prove the source can be restored before mutation.

2. Write the runbook contract

change:
  source: 3.84.2
  target: 3.95.2-01
  database: H2
  blobStore: file
  edition: Community
  rtoMinutes: 45
  targetOnlyWritesBeforeAcceptance: forbidden
preflight:
  - version-status-reviewed
  - target-release-notes-reviewed
  - crossed-upgrade-gates-recorded
  - coherent-recovery-checkpoint-verified
  - java21-and-truststore-plan
  - plugins-resolved
  - disk-and-file-handles-checked
acceptance:
  - startup
  - required-rebuilds
  - repository-hash
  - least-privilege
  - proxy-tls
  - representative-client
  - logs-clean
rollback:
  action: restore-pre-upgrade-checkpoint

3. Predict changes before performing them

Prediction Expected observation
Runtime moves to Java 21 Target process reports/uses Java 21; old JVM truststore is not assumed
Crossing 3.85 triggers search rebuild action Documented task scheduled/completed after target startup
Application binaries change New install/image version; persistent data remains the same logical instance
Artifact bytes should not change Pre/post SHA-256 matches for selected hosted assets

4. Build checkpoint and baseline fingerprint

from pathlib import Path
import json, hashlib, shutil
root=Path('ch27-checkpoint')
if root.exists(): shutil.rmtree(root)
(root/'source/blobs').mkdir(parents=True)
(root/'source/blobs/a.bin').write_bytes(b'CH27 checkpoint bytes\n')
state={'nexus':'3.84.2','java':17,'db':'H2','repo':'ch27-hosted','role':'ch27-reader'}
(root/'source/state.json').write_text(json.dumps(state,indent=2)+'\n')
shutil.copytree(root/'source',root/'recovery')
def fingerprint(d):
 out={}
 for p in sorted(d.rglob('*')):
  if p.is_file(): out[p.relative_to(d).as_posix()]=hashlib.sha256(p.read_bytes()).hexdigest()
 return out
base=fingerprint(root/'source')
(root/'baseline.json').write_text(json.dumps(base,indent=2)+'\n')
assert base==fingerprint(root/'recovery')
print('RECOVERY CHECKPOINT: VERIFIED')

5. Gate engine with one injected failure

from pathlib import Path
import json
root=Path('ch27-checkpoint')
gates={
 'releaseNotes':True,'upgradePath':True,'backup':True,'java21':True,
 'truststoreReady':False,'pluginsResolved':True,'disk':True,'database':True
}
(root/'gates-1.json').write_text(json.dumps(gates,indent=2)+'\n')
blocked=[k for k,v in gates.items() if not v]
print('GO/NO-GO:', 'NO-GO' if blocked else 'GO', blocked)
assert blocked==['truststoreReady']

The injected failure represents a CA that existed only in the old JVM truststore. The correct decision is NO-GO.

6. Correct the failure without bypassing TLS

from pathlib import Path
import json
root=Path('ch27-checkpoint')
g=json.loads((root/'gates-1.json').read_text())
g['truststoreReady']=True
assert all(g.values())
(root/'gates-2.json').write_text(json.dumps(g,indent=2)+'\n')
print('GO/NO-GO: GO')

7. Model the upgrade and post-upgrade task

from pathlib import Path
import json, shutil
root=Path('ch27-checkpoint')
shutil.copytree(root/'source',root/'target')
s=json.loads((root/'target/state.json').read_text())
s.update({'nexus':'3.95.2-01','java':21,'searchRebuild':'COMPLETE','truststoreReady':True})
(root/'target/state.json').write_text(json.dumps(s,indent=2)+'\n')
print('target started:',s['nexus'])

8. Validate the target

from pathlib import Path
import json, hashlib
root=Path('ch27-checkpoint')
base=json.loads((root/'baseline.json').read_text())
target=root/'target'
checks={
 'nexusVersion':json.loads((target/'state.json').read_text())['nexus']=='3.95.2-01',
 'java21':json.loads((target/'state.json').read_text())['java']==21,
 'searchRebuild':json.loads((target/'state.json').read_text())['searchRebuild']=='COMPLETE',
 'artifactHash':hashlib.sha256((target/'blobs/a.bin').read_bytes()).hexdigest()==base['blobs/a.bin'],
 'leastPrivilege':True,'proxyTls':True,'representativeClient':True,'logsClean':True
}
assert all(checks.values())
(root/'acceptance.json').write_text(json.dumps(checks,indent=2)+'\n')
print('TARGET ACCEPTANCE: PASS')

9. Rollback decision point

Because the drill forbids target-only writes before acceptance, rollback remains clean. To prove it, compare the recovery checkpoint to the original baseline:

from pathlib import Path
import hashlib, json
root=Path('ch27-checkpoint')
base=json.loads((root/'baseline.json').read_text())
out={}
for p in sorted((root/'recovery').rglob('*')):
 if p.is_file(): out[p.relative_to(root/'recovery').as_posix()]=hashlib.sha256(p.read_bytes()).hexdigest()
assert out==base
print('ROLLBACK SOURCE: UNCHANGED AND VERIFIED')

10. Evidence packet

  • source version/runtime/database/blob inventory
  • version-status and release-note review record
  • crossed-threshold list
  • backup/recovery fingerprint
  • gates-1.json NO-GO evidence
  • gates-2.json corrected GO evidence
  • post-upgrade task evidence
  • acceptance.json
  • representative client/TLS/auth output
  • rollback-source fingerprint

11. Cleanup / production handoff

After preserving sanitized evidence, delete only the disposable ch27-checkpoint lab. For a real production upgrade, keep the recovery set immutable until the defined rollback window closes. Do not delete it merely because the target has been running for a few minutes.

12. What this adds to the operating model

Chapter 27 adds a repeatable upgrade control: every release change has a source fingerprint, current documentation review, threshold evaluation, recovery proof, rehearsal, go/no-go gates, behavioral validation, and restore-based rollback. Chapter 28 can now build HA/resiliency on top of a supported, intentionally upgraded platform rather than multiplying nodes around an unknown version state.

Knowledge check

Why was target-only publication forbidden before acceptance?

What caused the intentional NO-GO?

What did the artifact hash prove?

Why is the search rebuild part of acceptance?

What is the rollback mechanism in the runbook?

Summary and next step

The checkpoint integrated the chapter into a complete runbook and proved both a NO-GO decision and a successful corrected upgrade without weakening security controls.

Next, Chapter 28 moves from a supported single-instance operating model to resiliency, HA topology, shared state, node coordination, and failure recovery.

Official references and version notes

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.