Chapter 29Lesson 02~175 minutes

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.

Release StrategyLTA / CurrentUpgrade PathsMigrationRollback

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 ceTaskId and 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.
Authorized disposable resources only. Never point this Compose file at a production database. Use fake source and lab-only credentials. Do not delete the source database until the target passes all acceptance checks.

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?

Does direct 26.7 → 26.9 mean 26.8 documentation is irrelevant?

Why is the broken 25.9 → 26.9 example stopped in preflight?

What does target scanner exit 0 fail to prove?

When is it safe to remove the source database/backup?

Next lesson

Choose release, deployment, scanner and plugin strategies deliberately

Lesson 3 turns the rehearsal into durable design choices around LTA/current releases, replacement deployments, scanners, plugins, APIs and rollout sequencing.

Official references and version notes

Version and compatibility note

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.

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