Chapter 04Lesson 05~175 minutes

Checkpoint Lab — Docker, Containers, Persistent Storage, and Production Deployment

Operate a disposable containerized SonarQube stack, prove persistence across container recreation, diagnose a reversible network mistake, and produce an edition/version-aware deployment dossier with guarded teardown.

CheckpointArchitecture dossierFailure injectionPersistenceGuarded teardown

Learning objectives

  • Build an edition/version-aware container deployment dossier before running the lab.
  • Predict image, network, volume, database, log, and health state changes and verify them independently.
  • Prove that DB-backed SonarQube state survives replacement of the SonarQube container.
  • Inject and diagnose a wrong database-host network configuration while preserving first-failure evidence.
  • Perform a guarded teardown and clearly separate lab persistence proof from real backup, upgrade, HA, and disaster-recovery requirements.

1. Checkpoint charter and assumptions

Build exactly one local lab stack using sonarqube:26.9.0.129388-community and postgres:17.11. Record the SonarQube image digest you actually pull, Docker Engine/Desktop version, host OS/runtime model, CPU/RAM allocation, database image digest, network name, volume names, and loopback port.

Authorization and scope: this lab may run only on a personal/disposable Docker environment. Use fake credentials. Do not point it at an employer/customer database, shared registry secret, public SonarQube instance, or production Docker daemon.

2. Write predictions before mutation

At minimum predict and later verify these state changes:

  1. After creating sq_net and named volumes, Docker metadata changes but no SonarQube application state exists yet.
  2. After PostgreSQL starts, the sq_pgdata volume becomes the durable database store.
  3. After SonarQube starts, data/extensions/log volumes become attached and the application reaches UP.
  4. After a manual project is created, that project should survive removal/recreation of sq-lab because PostgreSQL remains attached.
  5. After replacing the JDBC host with localhost, the new SonarQube container should fail to reach the sibling database even though PostgreSQL remains healthy.

3. Preflight and evidence directory

mkdir -p sq-ch04-evidence
docker version > sq-ch04-evidence/docker-version.txt
docker info > sq-ch04-evidence/docker-info.txt
docker ps -a --filter name=sq-lab --filter name=sq-db
docker volume ls --filter name='sq_'
docker network ls --filter name='^sq_net$'

On Linux also record vm.max_map_count, fs.file-max, ulimit -n, and ulimit -u. On Docker Desktop, record the Docker Desktop/runtime resource allocation instead of pretending the Windows/macOS host directly owns Linux container sysctls.

4. Build the clean stack

docker pull sonarqube:26.9.0.129388-community
docker pull postgres:17.11
docker image inspect sonarqube:26.9.0.129388-community --format '{{json .RepoDigests}}' > sq-ch04-evidence/sonarqube-repodigests.json
docker image inspect postgres:17.11 --format '{{json .RepoDigests}}' > sq-ch04-evidence/postgres-repodigests.json

docker network create sq_net
docker volume create sq_data
docker volume create sq_extensions
docker volume create sq_logs
docker volume create sq_pgdata

docker run -d --name sq-db --network sq_net --network-alias db \
  -e POSTGRES_DB=sonar -e POSTGRES_USER=sonar \
  -e POSTGRES_PASSWORD=FAKE_LOCAL_SQ_DB_ONLY \
  -v sq_pgdata:/var/lib/postgresql/data postgres:17.11

docker exec sq-db pg_isready -U sonar -d sonar

docker run -d --name sq-lab --network sq_net \
  -p 127.0.0.1:9000:9000 \
  -e SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar \
  -e SONAR_JDBC_USERNAME=sonar \
  -e SONAR_JDBC_PASSWORD=FAKE_LOCAL_SQ_DB_ONLY \
  -v sq_data:/opt/sonarqube/data \
  -v sq_extensions:/opt/sonarqube/extensions \
  -v sq_logs:/opt/sonarqube/logs sonarqube:26.9.0.129388-community

5. Verify clean health and record topology

docker inspect sq-lab > sq-ch04-evidence/sq-lab-inspect-initial.json
docker inspect sq-db > sq-ch04-evidence/sq-db-inspect-initial.json
docker network inspect sq_net > sq-ch04-evidence/sq-net-initial.json
docker logs sq-lab > sq-ch04-evidence/sq-lab-initial.log 2>&1
docker logs sq-db > sq-ch04-evidence/sq-db-initial.log 2>&1
curl -sS http://127.0.0.1:9000/api/system/status | tee sq-ch04-evidence/system-status-initial.json

Wait for UP. Then complete bootstrap login locally, change the default administrator password, and create the manual project sq-container-persistence-lab. Do not save the new password in the evidence packet.

6. Persistence proof

docker stop sq-lab
docker rm sq-lab

docker volume inspect sq_data sq_extensions sq_logs sq_pgdata > sq-ch04-evidence/volumes-before-recreate.json
docker exec sq-db pg_isready -U sonar -d sonar

docker run -d --name sq-lab --network sq_net \
  -p 127.0.0.1:9000:9000 \
  -e SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar \
  -e SONAR_JDBC_USERNAME=sonar \
  -e SONAR_JDBC_PASSWORD=FAKE_LOCAL_SQ_DB_ONLY \
  -v sq_data:/opt/sonarqube/data \
  -v sq_extensions:/opt/sonarqube/extensions \
  -v sq_logs:/opt/sonarqube/logs sonarqube:26.9.0.129388-community

Verify UP, sign in with the changed password, and confirm the project remains. Record the result as container-replacement persistence evidence, not as disaster-recovery evidence.

7. Inject one reversible network mistake

Preserve the healthy evidence, then replace only the SonarQube container with the intentionally wrong JDBC hostname:

docker stop sq-lab && docker rm sq-lab

docker run -d --name sq-lab --network sq_net \
  -p 127.0.0.1:9000:9000 \
  -e SONAR_JDBC_URL=jdbc:postgresql://localhost:5432/sonar \
  -e SONAR_JDBC_USERNAME=sonar \
  -e SONAR_JDBC_PASSWORD=FAKE_LOCAL_SQ_DB_ONLY \
  -v sq_data:/opt/sonarqube/data \
  -v sq_extensions:/opt/sonarqube/extensions \
  -v sq_logs:/opt/sonarqube/logs sonarqube:26.9.0.129388-community

Expected result: PostgreSQL remains healthy, but SonarQube fails to connect because localhost resolves inside sq-lab. Do not delete volumes or restart PostgreSQL while diagnosing.

8. Diagnose from independent evidence

docker exec sq-db pg_isready -U sonar -d sonar
docker network inspect sq_net > sq-ch04-evidence/sq-net-broken.json
docker inspect sq-lab > sq-ch04-evidence/sq-lab-broken.json
docker logs sq-lab > sq-ch04-evidence/sq-lab-broken.log 2>&1

Your written diagnosis should say: database readiness is good; both containers are on the intended network; JDBC configuration points to the wrong endpoint; the failed layer is application-to-database addressing, not database durability, search storage, quality policy, or SonarQube edition.

9. Repair the smallest layer and re-verify

docker stop sq-lab && docker rm sq-lab

docker run -d --name sq-lab --network sq_net \
  -p 127.0.0.1:9000:9000 \
  -e SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar \
  -e SONAR_JDBC_USERNAME=sonar \
  -e SONAR_JDBC_PASSWORD=FAKE_LOCAL_SQ_DB_ONLY \
  -v sq_data:/opt/sonarqube/data \
  -v sq_extensions:/opt/sonarqube/extensions \
  -v sq_logs:/opt/sonarqube/logs sonarqube:26.9.0.129388-community

Record final /api/system/status, logs, mounts, network membership, and project persistence. The repaired run should differ from the broken run only in the causal JDBC host configuration.

10. Required evidence packet

  • SonarQube and PostgreSQL exact tags plus observed digests;
  • Docker/runtime version and resource allocation;
  • Linux host-limit evidence where applicable;
  • network and volume manifests before/after recreation;
  • sanitized container inspect output;
  • healthy startup/status evidence;
  • proof that the manual project survives SonarQube container replacement;
  • broken-run first-failure logs and written causal diagnosis;
  • repaired-run status;
  • assumptions/limitations note explicitly saying this is not backup/restore, HA, TLS, or upgrade validation.

11. Production delta

A production handoff must add supported database backup/recovery, reverse proxy/TLS, secret manager, durable log/monitoring pipeline, capacity sizing, alerting, image/provenance policy, maintenance/upgrade path, plugin compatibility, vulnerability response, least-privilege Docker/orchestrator access, and tested recovery objectives. Data Center Edition and Kubernetes/OpenShift are separate architectures covered later.

12. Guarded teardown and final verification

docker ps -a --filter name=sq-lab --filter name=sq-db
docker volume ls --filter name='sq_'
docker network inspect sq_net --format '{{json .Containers}}'
# Manually verify names and that no retained lab work is needed.

docker stop sq-lab sq-db
docker rm sq-lab sq-db
docker network rm sq_net
docker volume rm sq_data sq_extensions sq_logs sq_pgdata

# Verify only expected lab resources were removed
docker ps -a --filter name=sq-lab --filter name=sq-db
docker volume ls --filter name='sq_'
docker network ls --filter name='^sq_net$'

Retain the evidence packet outside the removed volumes. Never generalize this teardown into a prune command.

Knowledge check

Which prediction demonstrates that you understand container replacement versus durable state?

During the intentionally broken run, what evidence proves PostgreSQL is not the failed layer?

Why is the project-survival check not a backup test?

What must be redacted before sharing container inspect output?

What is the safe teardown principle?

What chapter comes next and why?

Next lesson

From one Docker host to orchestrated platforms

Chapter 05 carries the same state, trust, persistence, and evidence model into Kubernetes and OpenShift, where Pods are even more disposable and storage/network/security ownership shifts to cluster primitives.

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against current SonarSource and Docker Hub primary material on 2026-09-07. Mandatory examples use SonarQube Community Build 26.9.0.129388 with the official sonarqube:26.9.0.129388-community image and PostgreSQL postgres:17.11, which is inside the currently supported PostgreSQL 14–18 range. The recorded multi-platform SonarQube image index digest at authoring time is sha256:62930c7f510534bb2bf551ca69ad3ee6f8e12b4116d394597c64cefccbb3313b; learners should inspect the digest they actually pull and retain it in their evidence packet. The SonarQube image supplies its own Java runtime; record java -version inside the running container when runtime provenance matters. No SonarScanner execution, third-party plugin, CI/provider integration, enterprise identity provider, or commercial feature is required by the mandatory Chapter 04 labs. Commercial Server/Data Center, Kubernetes, managed databases, paid CI, and enterprise identity are optional/later-course boundaries.

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.