Companion repository for the ankurm.com article. Every transcript in docs/output/ was produced by running this project; scripts/run-all.sh regenerates all of them. Verified against Spring Boot 4.1.1 / Framework 7.0.9 / Security 7.1.1 / Micrometer 1.17.1 / kafka-clients 4.2.1 on Temurin JDK 25.0.4.1+1.
170 lines
7.5 KiB
Markdown
Executable File
170 lines
7.5 KiB
Markdown
Executable File
# spring-boot-demo — Actuator in production
|
|
|
|
Companion repository for **[Spring Boot Actuator in Production](https://ankurm.com/spring-boot-actuator-production-endpoints-security-health-indicators/)** on ankurm.com.
|
|
|
|
Every status code, JSON body, byte count and timing figure in the article was produced by
|
|
running this project. The transcripts live in [`docs/output/`](docs/output) and are regenerated
|
|
by a single command.
|
|
|
|
---
|
|
|
|
## Versions
|
|
|
|
Verified with `mvn dependency:list` on the machine that produced `docs/output/` — see
|
|
[`docs/output/00-versions.txt`](docs/output/00-versions.txt).
|
|
|
|
| Component | Version | Notes |
|
|
|---|---|---|
|
|
| Spring Boot | 4.1.1 | GA 20 August 2026 |
|
|
| Spring Framework | 7.0.9 | via `spring-boot-starter-parent` |
|
|
| Spring Security | 7.1.1 | via `spring-boot-starter-parent` |
|
|
| `spring-boot-actuator` | 4.1.1 | |
|
|
| `spring-boot-health` | 4.1.1 | **new module in Boot 4** — `HealthIndicator` lives here now |
|
|
| `spring-boot-restclient` | 4.1.1 | **not pulled in by the web starter** |
|
|
| Micrometer | 1.17.1 | there is no Micrometer 2.x GA; see [`docs/02`](docs/02-boot-4-changes.md) |
|
|
| `kafka-clients` | 4.2.1 | what Boot 4.1.1 manages (4.3.1 is the latest on Central) |
|
|
| H2 | 2.4.240 | |
|
|
| JDK | Temurin 25.0.4.1+1 LTS | |
|
|
| Maven | 3.9.11 | |
|
|
|
|
---
|
|
|
|
## Quickstart
|
|
|
|
```bash
|
|
export JAVA_HOME=/path/to/jdk-25
|
|
mvn -DskipTests package
|
|
|
|
# defaults: only /actuator/health is exposed
|
|
java -jar target/actuator-production-1.0.0.jar
|
|
|
|
# every endpoint, no authentication - the configuration you should never ship
|
|
java -jar target/actuator-production-1.0.0.jar --spring.profiles.active=exposeall,open
|
|
|
|
# the configuration you should ship
|
|
java -jar target/actuator-production-1.0.0.jar --spring.profiles.active=secured
|
|
```
|
|
|
|
Credentials for every profile that requires them: **`ops` / `ops-password`**.
|
|
|
|
Regenerate every transcript in `docs/output/`:
|
|
|
|
```bash
|
|
JAVA_HOME=/path/to/jdk-25 ./scripts/run-all.sh
|
|
```
|
|
|
|
That takes roughly three minutes, most of which is the naive Kafka scenario blocking for its
|
|
full 60 seconds. That is the point of it.
|
|
|
|
---
|
|
|
|
## Profiles
|
|
|
|
| Profile | What it demonstrates |
|
|
|---|---|
|
|
| *(none)* | Boot defaults. Only `health` on the web, `show-details: never` |
|
|
| `exposeall` | `management.endpoints.web.exposure.include: "*"` |
|
|
| `open` | A `permitAll` security chain — the misconfiguration, kept on purpose |
|
|
| `secured` | `EndpointRequest.toAnyEndpoint()` + `ROLE_ACTUATOR` + `when-authorized` details |
|
|
| `mgmtport` | Actuator on port 9001, base path `/manage`, bound to loopback |
|
|
| `details` | `show-details: always` — the full component breakdown |
|
|
| `groups` | `liveness` / `readiness` / `startup` groups wired correctly |
|
|
| `kafkanaive` | The textbook `AdminClient` health check, so its 60-second block can be timed |
|
|
|
|
Profiles compose: `--spring.profiles.active=exposeall,open`.
|
|
|
|
---
|
|
|
|
## Endpoints
|
|
|
|
Read from the running application, not from the documentation — see
|
|
[`docs/output/02-endpoint-catalogue.txt`](docs/output/02-endpoint-catalogue.txt).
|
|
|
|
| Endpoint | Web-exposed by default | `access` default | Notes |
|
|
|---|---|---|---|
|
|
| `health` | **yes** | `unrestricted` | the only one exposed out of the box |
|
|
| `info` | no | `unrestricted` | Boot 4.1 added `process.*` fields |
|
|
| `beans` | no | `unrestricted` | 426 beans in this app, with types and wiring |
|
|
| `conditions` | no | `unrestricted` | the auto-configuration report |
|
|
| `configprops` | no | `unrestricted` | values masked like `env` |
|
|
| `env` | no | `unrestricted` | **masks every value** unless `show-values` says otherwise |
|
|
| `loggers` | no | `unrestricted` | has a `POST` — a write endpoint |
|
|
| `mappings` | no | `unrestricted` | every URL your app serves |
|
|
| `metrics` | no | `unrestricted` | from `spring-boot-micrometer-metrics` |
|
|
| `prometheus` | no | `unrestricted` | needs `micrometer-registry-prometheus` |
|
|
| `sbom` | no | `unrestricted` | |
|
|
| `scheduledtasks` | no | `unrestricted` | |
|
|
| `threaddump` | no | `unrestricted` | two operations: JSON and `text/plain` |
|
|
| `heapdump` | no | **`none`** | `include: "*"` is **not** enough |
|
|
| `shutdown` | no | **`none`** | `include: "*"` is **not** enough |
|
|
| `startup` | no | `unrestricted` | needs a `BufferingApplicationStartup` |
|
|
| `httpexchanges` | no | `unrestricted` | needs an `HttpExchangeRepository` bean |
|
|
| `auditevents` | no | `unrestricted` | needs an `AuditEventRepository` bean |
|
|
| `logfile` | no | `unrestricted` | needs `logging.file.name` |
|
|
| `caches`, `flyway`, `liquibase`, `quartz`, `sessions`, `integrationgraph` | no | `unrestricted` | conditional on the relevant module |
|
|
| `diag` | no | `unrestricted` | **this repository's own** — delete before shipping |
|
|
|
|
`heapdump` and `shutdown` are the only two endpoints whose `access` defaults to `none`. That
|
|
list came from Spring Boot's own `spring-configuration-metadata.json`, not from a blog.
|
|
|
|
---
|
|
|
|
## Documentation
|
|
|
|
| # | Chapter |
|
|
|---|---|
|
|
| 01 | [What Actuator actually exposes](docs/01-what-actuator-exposes.md) |
|
|
| 02 | [What changed in Spring Boot 4](docs/02-boot-4-changes.md) |
|
|
| 03 | [The endpoint catalogue](docs/03-endpoint-catalogue.md) |
|
|
| 04 | [Securing Actuator](docs/04-securing-actuator.md) |
|
|
| 05 | [Custom health indicators](docs/05-custom-health-indicators.md) |
|
|
| 06 | [Health indicator failure modes](docs/06-health-indicator-failure-modes.md) |
|
|
| 07 | [Groups, probes and Kubernetes](docs/07-groups-and-probes.md) |
|
|
| 08 | [The diagnostics endpoint](docs/08-diagnostics.md) |
|
|
| 09 | [Testing Actuator](docs/09-testing-actuator.md) |
|
|
|
|
---
|
|
|
|
## Captured output
|
|
|
|
| File | Scenario |
|
|
|---|---|
|
|
| [`00-versions.txt`](docs/output/00-versions.txt) | resolved dependency versions |
|
|
| [`01-default-exposure.txt`](docs/output/01-default-exposure.txt) | Actuator with zero configuration |
|
|
| [`02-endpoint-catalogue.txt`](docs/output/02-endpoint-catalogue.txt) | every exposed endpoint, from the running app |
|
|
| [`03-open-actuator-leak.txt`](docs/output/03-open-actuator-leak.txt) | what an anonymous caller really gets |
|
|
| [`04-heapdump-leak.txt`](docs/output/04-heapdump-leak.txt) | 59 MB, plaintext credentials inside |
|
|
| [`05-secured-matrix.txt`](docs/output/05-secured-matrix.txt) | the full authorisation matrix |
|
|
| [`06-management-port.txt`](docs/output/06-management-port.txt) | port 9001, and what it isolates |
|
|
| [`07-custom-health-indicators.txt`](docs/output/07-custom-health-indicators.txt) | DB, Kafka and external API |
|
|
| [`08-groups-and-probes.txt`](docs/output/08-groups-and-probes.txt) | liveness 200 while readiness 503 |
|
|
| [`09-kafka-timeout.txt.tuned`](docs/output/09-kafka-timeout.txt.tuned) | 1.6 s |
|
|
| [`09-kafka-timeout.txt.naive`](docs/output/09-kafka-timeout.txt.naive) | **60.2 s** |
|
|
| [`10-slow-upstream.txt`](docs/output/10-slow-upstream.txt) | a read timeout doing its job |
|
|
|
|
Status codes and bodies are reproducible. Timing figures are indicative and drift between
|
|
machines — except the 60-second one, which is a Kafka default and lands on 60.0 s every time.
|
|
|
|
---
|
|
|
|
## Layout
|
|
|
|
```
|
|
pom.xml
|
|
scripts/
|
|
run-all.sh regenerate everything below docs/output/
|
|
run.sh / stop.sh start and stop with given profiles
|
|
demo-*.sh one script per captured scenario
|
|
src/main/java/com/ankurm/actuator/
|
|
health/ OrdersDatabase, Kafka, ExternalApi indicators + UpstreamState
|
|
config/ Secured, Open and baseline security chains
|
|
web/ DiagnosticsEndpoint, stub upstream, business controller
|
|
src/test/java/ contract tests for the surprising behaviour
|
|
docs/ numbered chapters
|
|
docs/output/ captured real output
|
|
```
|
|
|
|
## Licence
|
|
|
|
MIT — see [LICENSE](LICENSE).
|