Marketplace, Plugins, Extension Risks, and Compatibility Management: Guided Hands-On Workflow
Inventory a disposable Community Build instance, stage one currently compatible nonessential plugin with provenance and checksum evidence, observe startup/capability changes, then remove it and prove rollback.
Learning objectives
- Create an exact disposable Community Build 26.9 container baseline and preserve its plugin/startup evidence.
- Select a nonessential plugin only after current compatibility, source, checksum, license, and maintenance preflight succeeds.
- Install one exact JAR manually into the isolated server, perform the required restart, and distinguish JAR presence from successful plugin loading.
- Verify the changed capability through startup logs and installed-plugin evidence rather than assuming a copied file is active.
- Remove the plugin, restart deliberately, and prove the server returns to its baseline plugin inventory and healthy state.
- Abort before installation when checksum/provenance or compatibility evidence fails.
1. Mandatory lab contract
| Item | Lab assumption |
|---|---|
| SonarQube | Community Build 26.9.0.129388 |
| Container image | sonarqube:26.9.0.129388-community |
| Port |
9026:9000, chosen to avoid the normal 9000
training instance
|
| Database | Container’s disposable evaluation database only; never production |
| Scanner | Not required to prove plugin loading; an optional smoke scan may use SonarScanner CLI 8.1.0.6389 |
| Java | Use the Java runtime inside the official Community Build image; Community Build 26.x requires Java 21+ when self-managed outside the image |
| Plugin | One currently compatible, nonessential, open-source lab plugin selected at run time from the current Plugin version matrix/Marketplace |
2. Start the isolated baseline
Use distinct container/volume names so cleanup cannot touch an existing SonarQube deployment. The bootstrap-check override below is acceptable only for this disposable learning container; it is not a production deployment recommendation.
export LAB_NAME="sq-ch26"
export LAB_URL="http://localhost:9026"
export SQ_IMAGE="sonarqube:26.9.0.129388-community"
mkdir -p evidence/baseline evidence/stage evidence/installed evidence/rollback
docker volume create sq-ch26-data
docker volume create sq-ch26-logs
docker volume create sq-ch26-extensions
docker run -d --name "$LAB_NAME" \
-p 9026:9000 \
-e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true \
-v sq-ch26-data:/opt/sonarqube/data \
-v sq-ch26-logs:/opt/sonarqube/logs \
-v sq-ch26-extensions:/opt/sonarqube/extensions \
"$SQ_IMAGE"
# Wait manually until status becomes UP; preserve each failure instead of looping forever.
curl -fsS "$LAB_URL/api/system/status" | tee evidence/baseline/system-status.json
curl -fsS "$LAB_URL/api/server/version" | tee evidence/baseline/server-version.txt
docker exec "$LAB_NAME" sh -c 'ls -lah /opt/sonarqube/extensions/plugins' \
| tee evidence/baseline/plugin-files.txt
docker logs "$LAB_NAME" > evidence/baseline/container.log 2>&1
Expected state: the exact image starts on port 9026, the API reports
UP, and the extensions volume contains only the
baseline files delivered or generated by the image. If startup is
not clean, stop here. A plugin experiment cannot diagnose a server
that was already unhealthy.
3. Select one plugin without hard-coding a stale tutorial artifact
Open the current Community Build Plugin version matrix or Marketplace from this disposable server. Choose a nonessential open-source plugin that explicitly lists compatibility with the exact Community Build release. The course intentionally does not hard-code a third-party JAR version: hard-coding today’s plugin would turn into tomorrow’s abandoned-plugin tutorial.
Record these shell variables from the verified current release. They are values, not secrets:
export PLUGIN_KEY="<manifest-plugin-key>"
export PLUGIN_VERSION="<exact-version>"
export PLUGIN_JAR="<exact-file-name.jar>"
export PLUGIN_URL="<immutable-release-asset-url>"
export EXPECTED_SHA256="<publisher-or-independently-recorded-sha256>"
export PLUGIN_LICENSE="<license-name>"
export PLUGIN_SOURCE="<canonical-source-repository>"
4. Stage and verify before copying anything into SonarQube
curl -fL "$PLUGIN_URL" -o "evidence/stage/$PLUGIN_JAR"
ACTUAL_SHA256="$(sha256sum "evidence/stage/$PLUGIN_JAR" | awk '{print $1}')"
printf 'expected=%s\nactual=%s\n' "$EXPECTED_SHA256" "$ACTUAL_SHA256" \
| tee evidence/stage/checksum.txt
test "$ACTUAL_SHA256" = "$EXPECTED_SHA256" || {
echo "ABORT: plugin checksum mismatch" | tee evidence/stage/preflight-failure.txt
exit 42
}
# A JAR is a ZIP. Capture manifest metadata without executing it.
unzip -p "evidence/stage/$PLUGIN_JAR" META-INF/MANIFEST.MF \
| tee evidence/stage/manifest.txt
grep -E '^(Plugin-Key|Plugin-Name|Plugin-Version|Plugin-License|Sonar-Version|Plugin-RequiredForLanguages):' \
evidence/stage/manifest.txt | tee evidence/stage/manifest-governance.txt
cat > evidence/stage/provenance.txt <<EOF
plugin_key=$PLUGIN_KEY
plugin_version=$PLUGIN_VERSION
artifact=$PLUGIN_JAR
url=$PLUGIN_URL
sha256=$ACTUAL_SHA256
license=$PLUGIN_LICENSE
source=$PLUGIN_SOURCE
compatibility=verified against current Community Build plugin version matrix
EOF
This inspection is intentionally read-only. A manifest is publisher-supplied metadata, so compare it to the external release and source record rather than treating the manifest as self-authenticating proof.
5. Controlled first failure: prove the integrity gate actually stops the change
Before the successful install, deliberately test the preflight with a fake digest. This creates useful failure evidence without making the server unhealthy.
REAL_SHA256="$EXPECTED_SHA256"
EXPECTED_SHA256="$(printf '0%.0s' {1..64})"
ACTUAL_SHA256="$(sha256sum "evidence/stage/$PLUGIN_JAR" | awk '{print $1}')"
if test "$ACTUAL_SHA256" = "$EXPECTED_SHA256"; then
echo "UNEXPECTED: mismatch test did not fail"; exit 99
else
printf 'PRE-INSTALL BLOCKED\nexpected=%s\nactual=%s\n' \
"$EXPECTED_SHA256" "$ACTUAL_SHA256" \
| tee evidence/stage/deliberate-checksum-failure.txt
fi
EXPECTED_SHA256="$REAL_SHA256"
The correct repair is to restore the verified digest—not to disable the checksum step. Notice that the SonarQube container has not been mutated, so failure isolation is strong.
6. Install the verified artifact and perform the required restart
A plugin change requires a restart to take effect. This is a deliberate lifecycle action on a disposable server, not a blanket “restart until it works” troubleshooting shortcut.
# Preserve an independent copy outside the extensions volume for rollback/audit.
cp "evidence/stage/$PLUGIN_JAR" "evidence/rollback/$PLUGIN_JAR"
# Copy only the verified bytes.
docker cp "evidence/stage/$PLUGIN_JAR" \
"$LAB_NAME:/opt/sonarqube/extensions/plugins/$PLUGIN_JAR"
docker exec "$LAB_NAME" sh -c \
'ls -lah /opt/sonarqube/extensions/plugins' \
| tee evidence/installed/plugin-files-before-restart.txt
docker restart "$LAB_NAME" | tee evidence/installed/restart.txt
# Wait for UP; preserve logs if it does not return.
curl -fsS "$LAB_URL/api/system/status" | tee evidence/installed/system-status.json
docker logs "$LAB_NAME" > evidence/installed/container.log 2>&1
grep -Ei "plugin|deploy|$PLUGIN_KEY|$PLUGIN_VERSION" \
evidence/installed/container.log \
| tail -n 100 > evidence/installed/plugin-startup-excerpt.txt || true
Three facts are distinct: the file exists, SonarQube restarted, and the plugin loaded successfully. Require all three. If the startup fails, preserve the entire log, remove only the newly introduced JAR, and follow the rollback procedure below. Do not delete all extensions or data volumes.
7. Verify capability and runtime evidence
Verify the plugin in Administration → Marketplace/installed plugins or through the current installed-plugin Web API with a bounded read-capable user token. Also verify the plugin’s intended benign capability—for example, a language/rule repository becoming visible—without pointing the lab at proprietary source.
# If forced authentication is enabled, create a disposable read-capable user token
# in this lab and export it without printing it.
curl -fsS -H "Authorization: Bearer $SONAR_API_TOKEN" \
"$LAB_URL/api/plugins/installed" \
| python -m json.tool \
| tee evidence/installed/plugins-installed.json
# Optional read-only rule repository inventory for analyzer plugins:
curl -fsS -H "Authorization: Bearer $SONAR_API_TOKEN" \
"$LAB_URL/api/rules/repositories" \
| python -m json.tool \
> evidence/installed/rule-repositories.json
Do not infer that a plugin works merely because the dashboard
renders. A scanner-side analyzer should also be validated by a tiny
synthetic source fixture if its real purpose is analysis. Preserve
the scanner process result separately from
report-task.txt/ceTaskId, Compute Engine
completion, and any resulting Quality Gate.
8. Remove and prove return to baseline
docker exec "$LAB_NAME" sh -c \
"rm -f '/opt/sonarqube/extensions/plugins/$PLUGIN_JAR'"
docker restart "$LAB_NAME" | tee evidence/rollback/restart.txt
curl -fsS "$LAB_URL/api/system/status" | tee evidence/rollback/system-status.json
docker exec "$LAB_NAME" sh -c 'ls -lah /opt/sonarqube/extensions/plugins' \
| tee evidence/rollback/plugin-files.txt
docker logs "$LAB_NAME" > evidence/rollback/container.log 2>&1
diff -u evidence/baseline/plugin-files.txt evidence/rollback/plugin-files.txt \
> evidence/rollback/plugin-inventory.diff || true
Review the diff rather than demanding byte-for-byte equality from
transient timestamps. The acceptance criterion is that the
introduced JAR is absent, the server is UP, the
baseline plugin set is restored, and the capability introduced only
by the plugin is no longer present.
9. Explicit cleanup
docker rm -f "$LAB_NAME"
docker volume rm sq-ch26-data sq-ch26-logs sq-ch26-extensions
unset SONAR_API_TOKEN EXPECTED_SHA256 PLUGIN_URL PLUGIN_JAR PLUGIN_KEY PLUGIN_VERSION
Keep the evidence/ directory if it is your checkpoint
packet. Delete only the volumes created by this lab. Never use broad
docker system prune as course cleanup.
10. Challenge: choose the correct layer
The JAR checksum and manifest are correct, but after restart the web
process reports an API incompatibility and never reaches
UP. Which state should you change first?
Answer pattern: preserve the exact server/plugin versions and startup logs; remove only the newly introduced plugin JAR; restart once to prove baseline recovery; then re-evaluate the compatibility matrix/plugin release. Do not edit the database/search index, install an older server blindly, or try random plugin versions.
Knowledge check
Why does the lab inject a bad checksum before the real install?
It proves the provenance gate blocks a bad artifact before server state changes, while preserving deterministic first-failure evidence.
A JAR exists in extensions/plugins. Is
installation proven?
No. The server must restart cleanly and logs/installed-plugin evidence must show the plugin loaded; capability evidence may also be required.
Why preserve an external copy of the exact plugin artifact?
It anchors provenance and rollback/audit evidence even if a mutable download location changes or disappears.
Should a failed plugin startup be repaired by clearing the database?
No. Preserve logs, remove only the introduced plugin, and prove baseline recovery first. Direct database/search edits are unsupported and destroy causal evidence.
What makes the deliberate restart acceptable here?
Plugin installation/removal requires restart, the server is isolated/disposable, the exact change and expected result are known, and evidence is preserved before and after.
Official references and version notes
-
Community Build — Installing a plugin
— Marketplace/manual paths, compatible JAR requirement,
extensions/plugins, Docker/Kubernetes guidance, restart and uninstall behavior. - Community Build — Using Marketplace — installed/compatible plugin discovery, internet/proxy behavior, Administer System requirement, pending install/update/uninstall and restart.
- Community Build — Plugin version matrix — current plugin-to-Community-Build compatibility table.
-
Community Build — Plugin basics
— Plugin API, scanner/Compute Engine/web extension points,
manifest metadata, dependencies and
Plugin-RequiredForLanguages. - Community Build — Performing the update — install compatible third-party plugins for the target release instead of blindly copying old plugin folders.
- SonarQube Server — Installing a plugin — current commercial manual-install model and Data Center application-node consistency/maintenance rules.
- Sonar Plugin API repository — independent Plugin API release stream and SonarQube ↔ Plugin API compatibility mappings.
-
Official SonarQube Docker image tags
— exact
26.9.0.129388-communityimage used by the checkpoint.
Rechecked 2026-09-08. Mandatory examples target SonarQube
Community Build 26.9.0.129388 using the official
sonarqube:26.9.0.129388-community Docker image.
Community Build supports Marketplace and manual plugin
installation; current commercial SonarQube Server documentation
requires manual installation. Third-party plugins are not provided
by Sonar and are installed at the operator’s risk. Compatibility
must be checked against the current Plugin version matrix and
plugin release documentation before every install/upgrade. The
checkpoint deliberately chooses the plugin at run time rather than
hard-coding a third-party version that may become abandoned. The
server image contains its supported Java runtime; outside the
image, current Community Build 26.x requires Java 21+. No external
database, production cluster, identity provider, reverse proxy, or
paid capability is required. Data Center plugin consistency is
taught as an optional/simulated commercial path.
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.