Release Cycle, LTA Strategy, Upgrade Paths, and Migration Planning: Guided Hands-On Workflow
Rehearse a real Community Build 26.7 to 26.9 update on a cloned PostgreSQL database, preserve the source environment, inspect migration evidence, and validate with a fresh analysis.
Learning objectives
- Build a disposable Community Build 26.7 source environment backed by PostgreSQL 17.11.
- Capture a complete compatibility and rollback manifest before changing server state.
- Back up the source database, restore it into a separate rehearsal database, and keep the source untouched.
- Start Community Build 26.9 against the cloned database and capture migration/startup evidence.
-
Rerun a representative analysis and correlate scanner upload with
the target
ceTaskIdand gate/result. - Diagnose a deliberately invalid cross-year update plan in preflight rather than discovering the mistake in production.
1. Lab contract and topology
The safest executable update rehearsal does not mutate the only copy
of your source database. This lab runs the older server against
sq29_source, creates a vendor-supported logical backup,
restores that dump into sq29_rehearsal, and starts the
26.9 target against the clone. If the migration fails, the 26.7
source database still exists as evidence and as a rollback anchor.
| Component | Exact lab identity | Purpose |
|---|---|---|
| Source server |
sonarqube:26.7.0.124771-community on
localhost:9007
|
Older supported 2026 baseline. |
| Target server |
sonarqube:26.9.0.129388-community on
localhost:9009
|
Current target. |
| Database | postgres:17.11-alpine |
Supported by current Community Build; same vendor/version across rehearsal. |
| Databases | sq29_source, sq29_rehearsal |
Source remains unchanged while target migrates the clone. |
| Scanner | SonarScanner CLI 8.1.0.6389 | Representative post-update validation. |
| Project | sq29-upgrade-lab |
Synthetic source only. |
2. Create the isolated source environment
Generate the database password in memory instead of committing one. The source and target SonarQube containers are never started at the same time against the same database.
mkdir -p sq29-upgrade-lab/evidence/{pre,backup,migration,post,broken}
cd sq29-upgrade-lab
export SQ29_DB_PASSWORD="$(python -c 'import secrets; print(secrets.token_urlsafe(24))')"
cat > compose.yml <<'EOF'
services:
db:
image: postgres:17.11-alpine
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: ${SQ29_DB_PASSWORD}
POSTGRES_DB: sq29_source
ports: ["54329:5432"]
volumes: ["sq29_pg:/var/lib/postgresql/data"]
networks: ["sq29"]
source:
image: sonarqube:26.7.0.124771-community
profiles: ["source"]
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sq29_source
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: ${SQ29_DB_PASSWORD}
ports: ["9007:9000"]
depends_on: [db]
networks: ["sq29"]
target:
image: sonarqube:26.9.0.129388-community
profiles: ["target"]
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sq29_rehearsal
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: ${SQ29_DB_PASSWORD}
ports: ["9009:9000"]
depends_on: [db]
networks: ["sq29"]
volumes:
sq29_pg:
networks:
sq29:
EOF
docker compose up -d db
docker compose --profile source up -d source
Wait for http://localhost:9007/api/system/status to
report an operational state. Record
docker image inspect output/digests and
docker compose ps. Create the disposable project in the
UI and create a project-analysis token; export it
as SONAR_TOKEN only in the current shell.
3. Establish a last-known-good analysis
mkdir -p repo/src
cd repo
git init
git config user.email "learner@example.invalid"
git config user.name "SonarQube Learner"
cat > src/math_demo.py <<'PY'
def add(a, b):
return a + b
PY
cat > sonar-project.properties <<'EOF'
sonar.projectKey=sq29-upgrade-lab
sonar.projectName=SQ Chapter 29 Upgrade Lab
sonar.sources=src
sonar.sourceEncoding=UTF-8
EOF
cat > .gitignore <<'EOF'
.scannerwork/
evidence/
EOF
git add . && git commit -m "chapter29 baseline"
git rev-parse HEAD | tee ../evidence/pre/revision.txt
export SONAR_HOST_URL=http://localhost:9007
sonar-scanner -X 2>&1 | tee ../evidence/pre/scanner.log
cp .scannerwork/report-task.txt ../evidence/pre/report-task.txt
SOURCE_TASK="$(sed -n 's/^ceTaskId=//p' .scannerwork/report-task.txt)"
echo "$SOURCE_TASK" > ../evidence/pre/ceTaskId.txt
curl -fsS -H "Authorization: Bearer $SONAR_TOKEN" \
"$SONAR_HOST_URL/api/ce/task?id=$SOURCE_TASK" > ../evidence/pre/ce-task.json
cd ..
Wait for the source task to reach SUCCESS. Capture
project measures/gate, server status, scanner version, PostgreSQL
version, installed plugin inventory, and any CI/IDE/API dependencies
you intend to validate after the update.
4. Determine path and read update notes
The official Community Build rule permits a direct 26.7 → 26.9 update because both versions are in 2026. Still read the release/update notes for 26.8 and 26.9. Convert every actionable note into the compatibility matrix instead of relying on memory.
component source target evidence / action
server 26.7.0.124771 26.9.0.129388 same-year direct path
postgres 17.11 17.11 supported; unchanged
server runtime image-bundled image-bundled record image digest/runtime
scanner CLI 8.1.0.6389 8.1.0.6389 validate post-update
plugins <inventory> <target-compatible> remove/update unknown compatibility
Web API <used endpoints> <V1/V2/deprecations> review deprecation evidence
CI/IDE <versions> <validated versions> representative checks
backup none required create before migration
5. Back up source database and create a clone
# Capture the backup while the source remains available; current Sonar guidance supports hot DB backups.
BACKUP_ID="sq29-$(date -u +%Y%m%dT%H%M%SZ)"
echo "$BACKUP_ID" | tee evidence/backup/id.txt
docker compose exec -T db pg_dump -U sonar -Fc sq29_source \
> "evidence/backup/${BACKUP_ID}.dump"
sha256sum "evidence/backup/${BACKUP_ID}.dump" \
| tee "evidence/backup/${BACKUP_ID}.sha256"
# Create an isolated rehearsal DB and restore the source point into it.
docker compose exec -T db createdb -U sonar sq29_rehearsal
docker compose exec -T db pg_restore -U sonar -d sq29_rehearsal --exit-on-error \
< "evidence/backup/${BACKUP_ID}.dump"
# Prove both DB identities exist before target migration.
docker compose exec -T db psql -U sonar -Atc \
"select datname from pg_database where datname like 'sq29_%' order by 1;" \
| tee evidence/backup/database-list.txt
The source DB is still the rollback anchor. The target will mutate
only sq29_rehearsal.
6. Stop source, start target, and capture migration evidence
docker compose stop source
docker compose --profile target up -d target
docker compose logs --no-color target > evidence/migration/target-startup.log
# Watch status without deleting logs or repeatedly restarting.
for i in $(seq 1 120); do
curl -fsS http://localhost:9009/api/system/status \
> evidence/migration/system-status.json 2>/dev/null && cat evidence/migration/system-status.json
sleep 5
done
If the UI indicates a database migration is required, open
http://localhost:9009/setup and perform the documented
setup/migration action for this disposable clone. Preserve the first
target startup log and the migration log/state before any retry. Do
not point 26.7 at sq29_rehearsal after migration.
7. Validate target with the same representative project
export SONAR_HOST_URL=http://localhost:9009
cd repo
TARGET_SHA="$(git rev-parse HEAD)"
sonar-scanner -X 2>&1 | tee ../evidence/post/scanner.log
cp .scannerwork/report-task.txt ../evidence/post/report-task.txt
TARGET_TASK="$(sed -n 's/^ceTaskId=//p' .scannerwork/report-task.txt)"
echo "$TARGET_TASK" > ../evidence/post/ceTaskId.txt
curl -fsS -H "Authorization: Bearer $SONAR_TOKEN" \
"$SONAR_HOST_URL/api/ce/task?id=$TARGET_TASK" > ../evidence/post/ce-task.json
cd ..
Verify independently: target system status; migrated project/history
exists; same Git SHA is analyzed; target Compute Engine task reaches
SUCCESS; expected profile/gate/new-code state remains
intentional; Web API calls used by automation still work; CI/IDE
checks run with their own credentials; and no target log shows an
unreviewed plugin/API compatibility problem.
8. Deliberately broken path: reject an unsupported cross-year jump before mutation
The real lab path is valid. The controlled failure is a plan validation using the documented Community Build year-boundary rule:
{
"source": "25.9.0.112764",
"target": "26.9.0.129388",
"proposed_path": ["25.9.0.112764", "26.9.0.129388"]
}
cat > validate_path.py <<'PY'
import json, sys
p=json.load(open('broken-plan.json'))
source_year=int(p['source'].split('.')[0])
target_year=int(p['target'].split('.')[0])
if target_year > source_year and len(p['proposed_path']) == 2:
print('BLOCKED: cross-year Community Build plan omits required year bridge; consult current update-path calculator/rules')
sys.exit(2)
print('plan requires normal documentation verification')
PY
python validate_path.py > evidence/broken/path-preflight.txt 2>&1 || true
Repair the plan, not the database: include the required bridge release(s) produced by the current documented rule/calculator, then rehearse each hop with its own target requirements and update notes. This is safer than discovering the mistake after a production schema migration begins.
9. Challenge: choose the owning layer
The 26.9 target UI loads, but your CI still fails while a manual API
request succeeds. Should you restore the database immediately? No.
Preserve the CI log, exact scanner/action version, effective
parameters and credentials; compare them with the successful API
call and target ceTaskId. A CI/scanner compatibility or
secret problem is not evidence of a database migration failure.
Knowledge check
Why migrate a cloned database in rehearsal instead of the source DB?
It preserves the last-known-good source as rollback evidence while proving the target's real schema migration and startup behavior.
Does direct 26.7 → 26.9 mean 26.8 documentation is irrelevant?
No. Direct means no installed intermediate hop; current guidance still requires reading release/update notes between source and target.
Why is the broken 25.9 → 26.9 example stopped in preflight?
It crosses a Community Build calendar-year boundary and therefore must follow the documented bridge-release rule/calculator.
What does target scanner exit 0 fail to prove?
It does not by itself prove Compute Engine success, gate pass, CI success, API compatibility or IDE/provider integration success.
When is it safe to remove the source database/backup?
Only after the defined acceptance window and retention policy say rollback is no longer required and the backup lifecycle is governed.
Official references and version notes
- SonarQube downloads — current Community Build, commercial current release, and current LTA identities.
- Community Build — Determining the update path — direct same-year updates; December/January bridge rules across calendar years; no Community Build LTA concept.
-
SonarQube Server — Release cycle model
— two-month releases, yearly LTA, active-version support model,
and
YYYY.Release.Patchversioning. - SonarQube Server — Determining the update path — intermediate LTA rules and update-path calculator.
- Community Build — Pre-update steps — read every intervening release note, back up the database, test first, and leave database disk headroom for migrations.
- Community Build — Performing the update — fresh installation/image, compatible plugins, configuration review, startup/migration and validation flow.
- Community Build — Post-update steps — scanner verification, database cleanup, service-path updates and API-deprecation review.
- Community Build — Other migration-related tasks — rollback requires restoring the pre-update database backup before switching back to the previous server.
- Community Build — Server host requirements — ZIP installations currently require JDK 21 or 25.
- Community Build — Database requirements — PostgreSQL 14–18 is supported in current Community Build.
- Plugin version matrix — verify every third-party plugin against the target before update.
- Deprecation policy — public APIs/features are deprecated before removal; migration planning must inventory those dependencies.
- SonarScanner CLI 8.1.0.6389 — scanner baseline used for post-update validation.
- Official SonarQube Docker tags — exact source/target image identities used by the rehearsal.
Rechecked 2026-09-08. Mandatory examples rehearse
SonarQube Community Build 26.7.0.124771 → 26.9.0.129388. The current target is
Community Build 26.9.0.129388 using official
sonarqube:26.7.0.124771-community and
sonarqube:26.9.0.129388-community images,
PostgreSQL 17.11, and
SonarScanner CLI 8.1.0.6389. Both Community Build
versions are in calendar year 2026, so the current update-path
rule permits a direct update; learners still read the 26.8 and
26.9 release/update notes before execution. Current commercial
references are
SonarQube Server 2026 Release 4.1 and
2026.1.5 LTA. Community Build has no LTA concept.
ZIP-hosted current SonarQube requires Java 21 or 25; the Docker
lab uses the image-bundled runtime. Current Community Build
supports PostgreSQL 14–18. Recheck exact release notes, target
host/database requirements, plugins, scanners, API deprecations,
and integrations immediately before any real update.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.