# Configuring Spring Boot Starter (/start/configuring-spring-boot-starter)

How to wire the Axelix Spring Boot Starter into a Spring Boot application so Axelix Master can discover it, read its state, and act on it.

This page shows how to wire the Axelix Spring Boot Starter into a [Spring Boot](https://spring.io/) application so
[Axelix Master](../../setting-up-master-ui/configuring-master/configuring-master.mdx) can discover it, read its state, and act on it — both from
the UI and through Master's bundled MCP (Model Context Protocol) server, which exposes the same data to AI agents. Pick
the starter module matching your Boot major version, expose the Axelix actuator endpoints (automatic as of Axelix
1.2.0), share the JWT signing key with Master, and tell Master where to find the instance.

## Before you start [#before-you-start]

A few facts worth pinning down before you copy any of the snippets below.

- **Three starter modules**, one per Spring Boot major version. Pick the one that matches the Boot version your
  application already uses:
    - `com.axelixlabs:axelix-spring-boot-2-starter` — Spring Boot **2.7+** (a hard floor, not just what we tested;
      built and tested against `2.7.18`, Spring Cloud `2021.0.9`, minimum JDK 11).
    - `com.axelixlabs:axelix-spring-boot-3-starter` — Spring Boot 3.x (built and tested against `3.0.13`, Spring Cloud
      `2022.0.4`, minimum JDK 17).
    - `com.axelixlabs:axelix-spring-boot-4-starter` — Spring Boot 4.x (built and tested against `4.0.6`, Spring Cloud
      `2025.1.0`, minimum JDK 17).
- **What the starter contributes**: a set of Axelix-specific actuator endpoints (`axelix-metadata`, `axelix-beans`,
  `axelix-loggers`, …), a JWT-based authorization filter that gates them, and an optional self-registration component
  that announces this instance to Master.
- **The Axelix build plugin is required too**: alongside the starter dependency, apply the Axelix Gradle or Maven build
  plugin. It stamps your build coordinates (`groupId`/`artifactId`/`version`) into the artifact, and Master identifies
  every application by those coordinates — an instance built without the plugin **cannot register**. See [What is the
  Axelix build plugin?](../../setting-up-spring-boot-service/build-plugin/what-is-build-plugin.mdx) and [Configuring the build
  plugin](../../setting-up-spring-boot-service/build-plugin/configuration.mdx).
- **Two consumers of the same data**: Master serves the Axelix UI at `/` and an MCP server at `/api/mcp` (controlled by
  `axelix.master.mcp-server.enabled`, on by default). Both read from the actuator endpoints the starter exposes, so
  wiring the starter up makes the instance visible to UI users *and* to MCP clients with no extra configuration on the
  starter side.
- **What Master needs to talk to the instance**: HTTP reachability to the application's actuator base path (default
  `/actuator`), the Axelix endpoints exposed through `management.endpoints.web.exposure.include`, and a JWT signing key
  identical to the one configured on Master — see [Authentication —
  JWT](../../setting-up-master-ui/configuring-master/configuring-master.mdx#authentication--jwt) for the master side.
- **Two ways Master finds the instance**:
    - **Master-side autodiscovery** — Master scans its environment (currently Kubernetes Services) and probes the
      actuator endpoint itself. The starter only needs to expose the endpoints; nothing additional to configure.
    - **Self-registration** — the starter posts a heartbeat to Master on a fixed interval. Use this when Master cannot
      scan the instance's environment (different network, no platform integration, plain VMs).

### Reactive (WebFlux) applications [#reactive-webflux-applications]

<UpcomingReleaseNotice />

Starting with Axelix 1.3.0, all three starters work in reactive applications built on Spring WebFlux, not only in
servlet (Spring MVC) applications. The starter detects which web stack your application runs on, so there is nothing
extra to add or configure: the dependency, the build plugin, and the properties on this page are the same for both.
The Axelix endpoints and the JWT authorization in front of them work the same way, including when Actuator runs on a
separate `management.server.port`.

One feature stays servlet-only. [Transaction monitoring](#transaction-monitoring), and with it the Transaction Control
and Entities Map pages, is not available in WebFlux applications.

## Add the dependency [#add-the-dependency]

Declare the starter coordinate matching your Boot major version.

:::note
The snippets below use the latest release, `AXELIX_VERSION_PLACEHOLDER`.
:::

<Tabs groupId="spring-boot-major" items={['Spring Boot 4.x', 'Spring Boot 3.x', 'Spring Boot 2.7']}>
  <Tab value="Spring Boot 4.x">

<Tabs groupId="build-tool" items={['Gradle (Kotlin DSL)', 'Gradle (Groovy)', 'Maven']}>
  <Tab value="Gradle (Kotlin DSL)">

```kotlin title="build.gradle.kts"
dependencies {
    implementation("com.axelixlabs:axelix-spring-boot-4-starter:AXELIX_VERSION_PLACEHOLDER")
}
```

  </Tab>
  <Tab value="Gradle (Groovy)">

```groovy title="build.gradle"
dependencies {
    implementation 'com.axelixlabs:axelix-spring-boot-4-starter:AXELIX_VERSION_PLACEHOLDER'
}
```

  </Tab>
  <Tab value="Maven">

```xml title="pom.xml"
<dependencies>
  <dependency>
    <groupId>com.axelixlabs</groupId>
    <artifactId>axelix-spring-boot-4-starter</artifactId>
    <version>AXELIX_VERSION_PLACEHOLDER</version>
  </dependency>
</dependencies>
```

  </Tab>
</Tabs>

  </Tab>
  <Tab value="Spring Boot 3.x">

<Tabs groupId="build-tool" items={['Gradle (Kotlin DSL)', 'Gradle (Groovy)', 'Maven']}>
  <Tab value="Gradle (Kotlin DSL)">

```kotlin title="build.gradle.kts"
dependencies {
    implementation("com.axelixlabs:axelix-spring-boot-3-starter:AXELIX_VERSION_PLACEHOLDER")
}
```

  </Tab>
  <Tab value="Gradle (Groovy)">

```groovy title="build.gradle"
dependencies {
    implementation 'com.axelixlabs:axelix-spring-boot-3-starter:AXELIX_VERSION_PLACEHOLDER'
}
```

  </Tab>
  <Tab value="Maven">

```xml title="pom.xml"
<dependencies>
  <dependency>
    <groupId>com.axelixlabs</groupId>
    <artifactId>axelix-spring-boot-3-starter</artifactId>
    <version>AXELIX_VERSION_PLACEHOLDER</version>
  </dependency>
</dependencies>
```

  </Tab>
</Tabs>

  </Tab>
  <Tab value="Spring Boot 2.7">

<Tabs groupId="build-tool" items={['Gradle (Kotlin DSL)', 'Gradle (Groovy)', 'Maven']}>
  <Tab value="Gradle (Kotlin DSL)">

```kotlin title="build.gradle.kts"
dependencies {
    implementation("com.axelixlabs:axelix-spring-boot-2-starter:AXELIX_VERSION_PLACEHOLDER")
}
```

  </Tab>
  <Tab value="Gradle (Groovy)">

```groovy title="build.gradle"
dependencies {
    implementation 'com.axelixlabs:axelix-spring-boot-2-starter:AXELIX_VERSION_PLACEHOLDER'
}
```

  </Tab>
  <Tab value="Maven">

```xml title="pom.xml"
<dependencies>
  <dependency>
    <groupId>com.axelixlabs</groupId>
    <artifactId>axelix-spring-boot-2-starter</artifactId>
    <version>AXELIX_VERSION_PLACEHOLDER</version>
  </dependency>
</dependencies>
```

  </Tab>
</Tabs>

  </Tab>
</Tabs>

The starter also depends on [Spring Boot
Actuator](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-actuator) if your application doesn't
already include `spring-boot-starter-actuator`, add it alongside.

:::tip[The build plugin is a separate step]
Applying the Axelix build plugin is required for every managed service, but it lives in its own section — see [What is
the Axelix build plugin?](../../setting-up-spring-boot-service/build-plugin/what-is-build-plugin.mdx) and [Configuring the build
plugin](../../setting-up-spring-boot-service/build-plugin/configuration.mdx).
:::

### Transitive dependencies [#transitive-dependencies]

The starter is built to add as little as possible to your classpath. Everything it integrates with — Spring Boot,
Actuator, Spring Kafka, Micrometer, Caffeine, Jackson — is compiled against but **not** brought
in: the starter uses whatever your application already has. The only libraries it brings in transitively are:

| Dependency | Why it is there |
|---|---|
| `org.slf4j:slf4j-api` | Logging. Already present in every Spring Boot application. |
| `io.jsonwebtoken:jjwt-api`, `jjwt-impl`, `jjwt-jackson` | Verifying the JWT that Master sends with every request. |

This list is meant to shrink. The starter is the one Axelix component that runs inside your services, and every
library it brings in is one more thing on your classpath to scan, patch, and keep compatible with the rest of your
application. The Axelix core team's position is that the starter should have **no external dependencies at all**, and
we are removing the remaining ones step by step — follow
[axelixlabs/axelix#1748](https://github.com/axelixlabs/axelix/issues/1748) for the progress.

#### JsonPath is no longer included [#jsonpath-is-no-longer-included]

<ReleasedInNotice version="1.2.0" />

Up to and including Axelix 1.1.x, the starter also brought in `com.jayway.jsonpath:json-path`, although it never used
it. Starting with Axelix 1.2.0, that dependency is removed — the first step towards the dependency-free starter
described in [axelixlabs/axelix#1748](https://github.com/axelixlabs/axelix/issues/1748).

If your application uses JsonPath in its own code and has been getting it only through the Axelix starter, declare it
explicitly when you upgrade. The version is managed by Spring Boot, so you do not need to specify one:

<Tabs groupId="build-tool" items={['Gradle (Kotlin DSL)', 'Gradle (Groovy)', 'Maven']}>
  <Tab value="Gradle (Kotlin DSL)">

```kotlin title="build.gradle.kts"
dependencies {
    implementation("com.jayway.jsonpath:json-path")
}
```

  </Tab>
  <Tab value="Gradle (Groovy)">

```groovy title="build.gradle"
dependencies {
    implementation 'com.jayway.jsonpath:json-path'
}
```

  </Tab>
  <Tab value="Maven">

```xml title="pom.xml"
<dependencies>
  <dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
  </dependency>
</dependencies>
```

  </Tab>
</Tabs>

Tests are not affected: `spring-boot-starter-test` brings in JsonPath on its own.

#### JSpecify is no longer included [#jspecify-is-no-longer-included]

<UpcomingReleaseNotice />

Up to and including Axelix 1.2.x, the starter also brought in `org.jspecify:jspecify`. The starter uses JSpecify only
for its nullness annotations, and it needs them only at compile time. Starting with Axelix 1.3.0, the dependency is
removed, as the next step towards the dependency-free starter described in
[axelixlabs/axelix#1748](https://github.com/axelixlabs/axelix/issues/1748).

The starter keeps working without JSpecify on the classpath, because the JVM ignores annotations whose classes are
missing at runtime. On Spring Boot 4 nothing changes at all, since Spring Framework 7 depends on JSpecify itself.

If your own code uses the JSpecify annotations and has been getting them only through the Axelix starter, declare the
dependency explicitly when you upgrade. Spring Boot 2 and 3 do not manage its version, so specify it:

<Tabs groupId="build-tool" items={['Gradle (Kotlin DSL)', 'Gradle (Groovy)', 'Maven']}>
  <Tab value="Gradle (Kotlin DSL)">

```kotlin title="build.gradle.kts"
dependencies {
    implementation("org.jspecify:jspecify:1.0.0")
}
```

  </Tab>
  <Tab value="Gradle (Groovy)">

```groovy title="build.gradle"
dependencies {
    implementation 'org.jspecify:jspecify:1.0.0'
}
```

  </Tab>
  <Tab value="Maven">

```xml title="pom.xml"
<dependencies>
  <dependency>
    <groupId>org.jspecify</groupId>
    <artifactId>jspecify</artifactId>
    <version>1.0.0</version>
  </dependency>
</dependencies>
```

  </Tab>
</Tabs>

## Actuator endpoints [#actuator-endpoints]

Starting with Axelix **1.2.0** there is nothing to configure by hand: on application startup the starter adds its own
actuator endpoints to `management.endpoints.web.exposure.include`, so Master can reach them out of the box. On older
versions the starter does not do this, and the endpoints must be listed manually — see [Exposing the endpoints on
Axelix below 1.2.0](#exposing-the-endpoints-on-axelix-below-120).

### What the starter exposes [#what-the-starter-exposes]

The starter exposes the following endpoints:

<table>
    <thead>
    <tr>
        <th>Endpoint</th>
        <th>Purpose</th>
        <th>Since</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td><code>axelix-metadata</code> <span className={styles.required}>required</span></td>
        <td>Service metadata; the one Master probes first</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-ping</code></td>
        <td>Health, memory, and transaction data Master polls for instances it already knows</td>
        <td><code>1.3.0</code> <em>upcoming</em></td>
    </tr>
    <tr>
        <td><code>axelix-details</code></td>
        <td>Summary information about the instance</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-beans</code></td>
        <td>Context beans</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-caches</code></td>
        <td>Caches (when a <code>CacheManager</code> is present)</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-conditions</code></td>
        <td>Auto-configuration conditions report</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-configprops</code></td>
        <td><code>@ConfigurationProperties</code></td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-env</code></td>
        <td>Environment and property sources</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-feign</code> <em>legacy</em></td>
        <td>Feign clients (when Feign is on the classpath). Removed in <code>1.3.0</code>, see below</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-gc</code></td>
        <td>Garbage collection</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-heap-dump</code></td>
        <td>Heap dump</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-loggers</code></td>
        <td>Logging levels</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-logs</code></td>
        <td>Recent log entries</td>
        <td><code>1.3.0</code> <em>upcoming</em></td>
    </tr>
    <tr>
        <td><code>axelix-metrics</code></td>
        <td>Metrics</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-scheduled-tasks</code></td>
        <td>Scheduled tasks</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-thread-dump</code></td>
        <td>Thread dump</td>
        <td><code>1.0.0</code></td>
    </tr>
    <tr>
        <td><code>axelix-dependencies</code></td>
        <td>Dependency SBOM</td>
        <td><code>1.2.0</code></td>
    </tr>
    </tbody>
</table>

The **Since** column names the Axelix starter release an endpoint first shipped in. `1.0.0` is the first Axelix release,
so an endpoint marked `1.0.0` has been there from the very beginning. A version marked _upcoming_ is not released yet:
the endpoint will become available in the next Axelix release, whether that is a minor or a major one.

Endpoints that don't apply to your application stay inert — the starter activates each one only when its prerequisites
are met (for example, `axelix-caches` without a `CacheManager` never comes up).

As of Axelix **1.2.0** the native `health` and `info` endpoints are no longer required, so the starter never exposes
them on its own (before 1.2.0 Axelix depended on them, and the starter force-exposed both). Only the endpoints you
list in `management.endpoints.web.exposure.include` plus the Axelix ones are exposed — add `health` and `info` to the
list yourself when you need them, including the `health` endpoint that Spring Boot would otherwise expose by default.

#### The `axelix-feign` endpoint is removed [#the-axelix-feign-endpoint-is-removed]

<UpcomingReleaseNotice />

Up to and including Axelix 1.2.x, the Spring Boot 3 and Spring Boot 4 starters also contributed an `axelix-feign`
endpoint that listed the Spring Cloud OpenFeign clients of the application. Axelix Master has never used it to
render anything, and it stayed around only as a leftover from the early days of the project. Starting with Axelix 1.3.0 the endpoint is removed from the starters, together with everything in Master
that was able to call it.

There is nothing to do when you upgrade. The only visible difference is that `/actuator/axelix-feign` no longer
exists on a service running starter 1.3.0 or newer, so if your own tooling calls that URL directly, it will get a
`404`. If you list the Axelix endpoints in `management.endpoints.web.exposure.include` by hand and `axelix-feign` is
among them, you can drop it from the list — leaving it there is harmless.

### Adding your own endpoints [#adding-your-own-endpoints]

If you need your own actuator endpoints, just list **only them** — the starter **merges** your list with its own instead
of replacing it.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
management:
  endpoints:
    web:
      exposure:
        include:
          - prometheus
```

  </Tab>
  <Tab value="application.properties">

```properties
management.endpoints.web.exposure.include=prometheus
```

  </Tab>
</Tabs>

The result is that `prometheus` and all the Axelix endpoints are exposed.

The `*` value is supported too — everything gets exposed and the starter leaves the list alone.

### Exposing the endpoints on Axelix below 1.2.0 [#exposing-the-endpoints-on-axelix-below-120]

<LegacyNotice version="1.2.0" />

Starters older than 1.2.0 do not add their endpoints to the exposure list themselves, so every Axelix endpoint you
want Master to read has to be listed in `management.endpoints.web.exposure.include` by hand. At minimum expose
`axelix-metadata` — without it the instance never appears in Master — and in practice the whole set, so every UI
section has data to show. The `health` and `info` endpoints are force-exposed by those starter versions on their own,
so you do not need to list them:

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
management:
  endpoints:
    web:
      exposure:
        include:
          - axelix-metadata
          - axelix-beans
          - axelix-caches
          - axelix-conditions
          - axelix-configprops
          - axelix-details
          - axelix-env
          - axelix-gc
          - axelix-heap-dump
          - axelix-loggers
          - axelix-metrics
          - axelix-scheduled-tasks
          - axelix-thread-dump
```

  </Tab>
  <Tab value="application.properties">

```properties
management.endpoints.web.exposure.include=axelix-metadata,axelix-beans,axelix-caches,axelix-conditions,axelix-configprops,axelix-details,axelix-env,axelix-gc,axelix-heap-dump,axelix-loggers,axelix-metrics,axelix-scheduled-tasks,axelix-thread-dump
```

  </Tab>
</Tabs>

These starter versions also contribute the legacy `axelix-feign` endpoint. It is left out of the list on purpose:
Master never reads it, and the endpoint is removed in Axelix 1.3.0 — see [above](#what-the-starter-exposes).

## Export Axelix metrics through Micrometer [#export-axelix-metrics-through-micrometer]

The `axelix-metrics` actuator endpoint powers the Axelix UI. In addition, the Spring Boot starters can publish a small
set of Axelix-specific meters into your application's own Micrometer `MeterRegistry`.

This export path switches on automatically when a `MeterRegistry` bean is present. There is no extra Axelix property for
it. The starter registers these meters:

<div className={styles.MetricsTable}>
| Meter | Type | Tags | Meaning |
|-------|------|------|---------|
| `axelix_transaction_duration` | `Timer` | `class`, `method` | Duration of each monitored `@Transactional` execution. The timer also publishes `0.5`, `0.95`, and `0.99` percentiles. |
| `axelix_transaction_queries` | `Counter` | `class`, `method` | Total number of SQL queries executed inside monitored transactions. |
| `axelix_transaction_external_calls` | `Counter` | `class`, `method` | Total number of external calls (e.g. `RestTemplate`) made inside monitored transactions. |
| `axelix_cache_requests` | `Counter` | `cache`, `result` | Total number of cache lookups recorded by Axelix-enhanced caches. |
| `axelix_cache_enabled` | `Gauge` | `cache` | Current runtime state of each enhanced cache: `1` when enabled, `0` when disabled. |
</div>

These meters complement the UI-facing actuator endpoints rather than replace them. The UI still reads
`axelix-metrics` and `axelix-caches`. Transaction monitoring data is not served by a dedicated actuator endpoint —
it travels to Master as part of the instance's registration metadata exposed through `axelix-metadata` (see
[Transaction monitoring](#transaction-monitoring)).

## Share the JWT signing key with Master [#share-the-jwt-signing-key-with-master]

Every Axelix actuator endpoint is gated by `JwtAuthorizationFilter`. The filter expects an HS256, HS384, or HS512 token
signed with the same key Master uses, so the two sides must agree on the algorithm and the secret. Use the same value
you set for `axelix.master.auth.jwt.signing-key` on Master.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    auth:
      jwt:
        algorithm: HMAC512
        signing-key: ${AXELIX_JWT_SIGNING_KEY}
        duration: 5m
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.auth.jwt.algorithm=HMAC512
axelix.sbs.auth.jwt.signing-key=${AXELIX_JWT_SIGNING_KEY}
axelix.sbs.auth.jwt.duration=5m
```

  </Tab>
</Tabs>

`duration` controls how long the starter-issued token used for self-registration heartbeats is valid, the default is five minutes,
after which the starter rotates it automatically.

:::warning
A signing key shared between Master and every monitored service is a sensitive secret. Inject it through an environment
variable, mounted file, or secret manager — not a checked-in YAML.
:::

### Running without a signing key [#running-without-a-signing-key]

<ReleasedInNotice version="1.2.0" />

Starting with Axelix 1.2.0, `algorithm` and `signing-key` are optional. When they are not set, the starter uses
`HMAC512` and the same built-in key that Master uses by default, so the service connects to a Master with default
settings without any key setup. While the built-in key is in use, the starter logs a warning at startup.

:::warning
The built-in key is publicly known. Anyone who can reach the service's actuator endpoints can sign a token with it and
call the Axelix endpoints, for example to read the environment or take a heap dump. Set your own
`axelix.sbs.auth.jwt.signing-key`, the same as on Master, before using Axelix beyond a local trial.
:::

## Let Master find the instance [#let-master-find-the-instance]

Pick one of the two paths below. They are mutually exclusive in practice — self-registration only adds latency if Master
can already scan the environment.

### Option A — Master-side autodiscovery [#option-a--master-side-autodiscovery]

When Master runs inside a Kubernetes cluster alongside your services, the simplest setup is to let Master scan the
namespace for Services and probe each one's `/actuator/axelix-metadata`. No further configuration is needed on the
starter side beyond exposing the actuator endpoints and sharing the JWT signing key. Configure the master-side scan with
`axelix.master.discovery.auto.*` — see [Discovery —
auto-discovery](../../setting-up-master-ui/configuring-master/configuring-master.mdx#discovery--auto-discovery) on the Master page.
For what Master does on each scan, see [How discovery works](../../product/discovery.mdx).

### Option B — Self-registration [#option-b--self-registration]

When Master cannot scan the instance's environment — a different cluster, a plain VM, an isolated network — switch on
self-registration in the starter. The instance then posts a heartbeat carrying its actuator URL to Master on a fixed
interval. Master accepts the post at `POST /api/internal/service/register`, which is also where you point
`axelix.sbs.discovery.master-url`. Starting with Axelix 1.3.0, the starter sends this full registration only once and
then switches to lightweight heartbeats, which go to an endpoint next to `master-url`. For the whole cycle, see [How
discovery works](../../product/discovery.mdx).

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    discovery:
      self-registration: true
      master-url: http://master.internal:2935/api/internal/service/register
      instance-actuator-url: http://my-app.internal:8080/actuator
      instance-name: orders-service
      heartbeat-interval: 15s
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.discovery.self-registration=true
axelix.sbs.discovery.master-url=http://master.internal:2935/api/internal/service/register
axelix.sbs.discovery.instance-actuator-url=http://my-app.internal:8080/actuator
axelix.sbs.discovery.instance-name=orders-service
axelix.sbs.discovery.heartbeat-interval=15s
```

  </Tab>
</Tabs>

A few details that catch people out:

- `instance-actuator-url` is the **base** URL of the service as Master will reach it from its own network — host and
  port only.
- `master-url` is the **full** URL including `/api/internal/service/register`. The starter posts to it verbatim.
  Master listens on `2935` by default since Axelix `1.2.0`; before that release the default port was `8080` — see
  [General](../../setting-up-master-ui/configuring-master/configuring-master.mdx#general).
- `instance-name` is the label Master shows for this instance in the UI and in MCP responses. Pick a name that survives
  restarts and replicas — typically the service name, not the pod name.
- Self-registration is accepted by Master out of the box (`axelix.master.discovery.self-registration.enabled` is `true`
  by default), set it to `false` if you want to refuse heartbeats. Master evicts an instance after
  `axelix.master.discovery.self-registration.eviction.heartbeat-timeout` of silence (`45s` by default), so keep
  `heartbeat-interval` comfortably under that.

## Sanitize sensitive property values [#sanitize-sensitive-property-values]

The Axelix `axelix-env` and `axelix-configprops` endpoints return property values to Master. To keep secrets out of
casual reads, the starter masks values it returns to callers that lack the right authority. The mechanism is the same
for both endpoints, controlled by a single starter property:

`axelix.sbs.endpoints.config.sanitized-properties`

- **Empty list (default)** — every value returned by `axelix-env` and `axelix-configprops` is replaced with `******` for
  callers without the relevant authority. The default is intentionally conservative: no opt-in needed to be safe.
- **Non-empty list** — only the listed keys are masked; everything else is returned raw to any caller. Use this when the
  conservative default is too aggressive and you want to mask only specific keys (passwords, tokens, signing keys).

Authority is checked per-endpoint:

- **`axelix-env`** — the `ENV_VALUES_READ` authority lets the caller see raw values.
- **`axelix-configprops`** — the `CONFIG_PROPS_VALUES_READ` authority lets the caller see raw values.

Both authorities are granted by Master's built-in **ADMIN** and **SUPER_ADMIN** roles. **VIEWER** and **EDITOR** always
go through the sanitization function. See [Roles and
authorities](../../setting-up-master-ui/authentication/authentication.mdx#roles-and-authorities) for the full matrix.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    endpoints:
      config:
        sanitized-properties:
          - database.password
          - spring.datasource.password
          - jwt.signing-key
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.endpoints.config.sanitized-properties[0]=database.password
axelix.sbs.endpoints.config.sanitized-properties[1]=spring.datasource.password
axelix.sbs.endpoints.config.sanitized-properties[2]=jwt.signing-key
```

  </Tab>
</Tabs>

:::note
The standard Spring Boot Actuator masking switches — `management.endpoint.env.show-values`,
`management.endpoint.env.keys-to-sanitize`, and their `configprops` equivalents — are **not** consulted.
:::

## Configuration reference [#configuration-reference]

Every starter property lives under the `axelix.sbs.*` namespace.

### Disabling the starter [#disabling-the-starter]

<ReleasedInNotice version="1.2.0" />

`axelix.sbs.enabled` is the global switch for the whole starter. It is `true` by default, so adding the dependency is
enough to activate everything described on this page. Set it to `false` and every Axelix auto-configuration backs off:
no Axelix actuator endpoints, no JWT authorization filter, no self-registration heartbeats, no Micrometer meters, and
no transaction monitoring. The application then starts as if the starter were not on the classpath, while the
dependency itself stays in the build.

Reach for this switch when you want Axelix off in a specific environment — for example locally or in a test profile —
without maintaining a separate dependency set. It is also a quick way to rule the starter out while troubleshooting
startup issues.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    enabled: false
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.enabled=false
```

  </Tab>
</Tabs>

The property is read once, during startup. As a result, changing it requires a restart of the application.

:::note
With the starter disabled, Master loses sight of the instance: autodiscovery probes to `/actuator/axelix-metadata`
fail, and self-registration heartbeats stop, so the instance disappears from the UI and from MCP responses.
:::

<div className={styles.SharedTable}>

| Property             | Default | Description                                                                                                                                                       |
|----------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.enabled` | `true`  | Global switch for the whole starter. Set to `false` to make every Axelix auto-configuration back off. Behaves identically on the Spring Boot 2, 3, and 4 starters. |

</div>

### Authentication — JWT [#authentication--jwt]

<div className={styles.SharedTable}>
| Property                          | Default                                              | Description                                                                                                                  |
|-----------------------------------|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.auth.jwt.algorithm`   | `HMAC512` (1.2.0+; required before) | Signing algorithm. One of `HMAC256`, `HMAC384`, or `HMAC512`. Must match Master's `axelix.master.auth.jwt.algorithm`.        |
| `axelix.sbs.auth.jwt.signing-key` | built-in key (1.2.0+; required before) | Shared secret used to verify Master-issued tokens and to sign self-registration heartbeats. Must match Master's signing key. |
| `axelix.sbs.auth.jwt.duration`    | `5m`                                                 | Validity of the self-registration token the starter issues itself. Tokens are rotated automatically before expiry.           |
</div>

### Self-registration [#self-registration]

Self-registration is **off by default** (`axelix.sbs.discovery.self-registration=false`) — leave it that way when Master can find the
instance through Master-side autodiscovery instead.

When `axelix.sbs.discovery.self-registration=true`, the following properties become **required**:

- `axelix.sbs.discovery.master-url`
- `axelix.sbs.discovery.instance-actuator-url`
- `axelix.sbs.discovery.instance-name`

The Master-side receiver of these heartbeats — endpoint, eviction timeout, eviction sweep schedule — is documented on
the Master page in [Discovery —
self-registration](../../setting-up-master-ui/configuring-master/configuring-master.mdx#discovery--self-registration). The JWT signing key
configured under `axelix.sbs.auth.jwt.signing-key` must match `axelix.master.auth.jwt.signing-key` on Master, otherwise
heartbeats are rejected with `401`.

<div className={styles.SharedTable}>

| Property                                     | Default                                              | Description                                                                                                                          |
|----------------------------------------------|------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.discovery.self-registration`     | `false`                                              | Master switch. Set to `true` to enable self-registration; leave `false` to rely on Master-side autodiscovery.                                  |
| `axelix.sbs.discovery.master-url`            | *(unset)* <span className="Required">required</span> | Full URL of Master's self-registration endpoint, e.g. `http://master:2935/api/internal/service/register`. Required when `self-registration=true`. |
| `axelix.sbs.discovery.instance-actuator-url` | *(unset)* <span className="Required">required</span> | Base URL of this service as reachable from Master (It is necessary to specify the postfix `/actuator`). Required when `self-registration=true`.   |
| `axelix.sbs.discovery.instance-name`         | *(unset)* <span className="Required">required</span> | Display name shown in Axelix UI and MCP responses. Required when `self-registration=true`.                                                        |
| `axelix.sbs.discovery.heartbeat-interval`    | `15s`                                                | Interval between heartbeats. Should be smaller than Master's eviction `heartbeat-timeout`.                                           |

</div>

### Endpoint value sanitization [#endpoint-value-sanitization]

<div className={styles.SharedTable}>

| Property                                           | Default        | Description                                                                                                                                                                                                                                                                                                            |
|----------------------------------------------------|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.endpoints.config.sanitized-properties` | *(empty list)* | Property keys whose values are masked as `******` for callers without `ENV_VALUES_READ` (for `axelix-env`) or `CONFIG_PROPS_VALUES_READ` (for `axelix-configprops`). Empty list means **all** values are masked. See [Sanitize sensitive property values](#sanitize-sensitive-property-values) for the matching rules. |

</div>

### Cache management [#cache-management]

<ReleasedInNotice version="1.2.0" />

Cache management is what powers the Caches page and the `axelix_cache_*` meters: the starter wraps every `CacheManager`
bean of your application in a proxy that records cache lookups and lets you enable, disable, and clear caches at
runtime, and it exposes that data through the `axelix-caches` actuator endpoint. It is **on by default** once the
starter is on the classpath.

Set `axelix.sbs.cache.management.enabled` to `false` to turn it off entirely — no `CacheManager` proxying, no
`axelix-caches` endpoint, and no cache meters. Your `CacheManager` beans are then left exactly as your application
defined them.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    cache:
      management:
        enabled: false
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.cache.management.enabled=false
```

  </Tab>
</Tabs>

<div className={styles.SharedTable}>

| Property                              | Default | Description                                                                                                                                                                  |
|---------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.cache.management.enabled` | `true`  | Master switch for cache management. Set to `false` to make the whole cache management auto-configuration back off. Behaves identically on the Spring Boot 2, 3, and 4 starters. |

</div>

:::note
With cache management disabled, the Caches page in Master has no data to show for this instance, and the cache MCP
tools fail for it.
:::

### Log entries [#log-entries]

<UpcomingReleaseNotice />

The `axelix-logs` endpoint serves the most recent log entries of your application, so you can read them without
access to the host. The starter keeps these entries in memory, starting from application startup. When the buffer is
full, each new entry pushes out the oldest one. Because the buffer lives in memory, it is empty again after a restart.

By default, the starter keeps the last 1000 entries. Raise `axelix.sbs.logs.max-entries` when a busy service logs
so much that 1000 entries cover only a few seconds. Lower it when memory is tight, because every kept entry stays on
the heap.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    logs:
      max-entries: 5000
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.logs.max-entries=5000
```

  </Tab>
</Tabs>

<div className={styles.SharedTable}>

| Property                      | Default | Description                                                                                                       |
|-------------------------------|---------|-------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.logs.max-entries` | `1000`  | Maximum number of the most recent log entries the starter keeps in memory. Must be greater than `0`. |

</div>

:::note
The starter checks the value once the application has started. If it is `0` or negative, startup fails with an
`IllegalArgumentException` saying `axelix.sbs.logs.max-entries must be positive`.
:::

### Transaction monitoring [#transaction-monitoring]

Transaction monitoring is what powers the Transaction Control page and the related meters: it wraps your
`@Transactional` methods, proxies the data source to count queries, cleans up the per-request transaction stack, and
registers the Hibernate N+1 integrator. The collected insights travel to Master inside the instance's registration
metadata (exposed through `axelix-metadata`), so there is no separate actuator endpoint to expose. It is **on by
default** once the starter is on the classpath.

Transaction monitoring works only in servlet (Spring MVC) applications. In a reactive (WebFlux) application the starter
doesn't turn it on, whatever the property says. See [Reactive (WebFlux) applications](#reactive-webflux-applications).

Set `axelix.sbs.transaction.monitoring.enabled` to `false` to turn it off entirely — no bean post-processors, no data
source proxying, no cleanup filter, and no Hibernate integrator. Use this to shave the associated overhead, to rule out
interference while troubleshooting your data source / Hibernate setup, or simply when you don't use the feature.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    transaction:
      monitoring:
        enabled: false
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.transaction.monitoring.enabled=false
```

  </Tab>
</Tabs>

<div className={styles.configTable}>

| Property                                     | Default | Description                                                                                                                                                     |
|----------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `axelix.sbs.transaction.monitoring.enabled`  | `true`  | Master switch for transaction monitoring. Set to `false` to make the whole transaction monitoring auto-configuration back off. Behaves identically on the Spring Boot 2, 3, and 4 starters. |

</div>

:::note
Disabling transaction monitoring also disables in-memory pagination detection described below, since that detection is
part of the same auto-configuration.
:::

### In-memory pagination detection [#in-memory-pagination-detection]

Detects Hibernate`s in-memory pagination caused by using
[setFirstResult/setMaxResults](https://docs.hibernate.org/orm/current/javadocs/org/hibernate/query/Query.html?#setMaxResults(int))
together with a collection fetch.

By default, transaction monitoring registers an appender that intercepts Hibernate warnings (HHH000104 in Hibernate 5.x
and HHH90003004 in Hibernate 6.x–7.3.x). The starter picks the appender that matches your application's active logging
system, so detection works whether you run on Logback or Log4j2:

- **Logback** — the starter registers
  `com.axelixlabs.axelix.sbs.spring.core.persistence.hibernate.pagination.LogbackInMemoryPaginationAppender`. This is the
  case for a default Spring Boot setup, where Logback is the logging system.
- **Log4j2** — when your application uses Log4j2 instead (typically after adding `spring-boot-starter-log4j2` and
  excluding the default Logback starter), the starter registers
  `com.axelixlabs.axelix.sbs.spring.core.persistence.hibernate.pagination.Log4j2InMemoryPaginationAppender`.

You don't choose between them — the starter detects the active logging system and registers the right appender for you.

Set `axelix.sbs.transaction.monitoring.in-memory-pagination-detection.enabled` to `false` if you want to prevent either
appender from being registered.

<Tabs groupId="spring-config" items={['application.yaml', 'application.properties']}>
  <Tab value="application.yaml">

```yaml
axelix:
  sbs:
    transaction:
      monitoring:
        in-memory-pagination-detection:
          enabled: false
```

  </Tab>
  <Tab value="application.properties">

```properties
axelix.sbs.transaction.monitoring.in-memory-pagination-detection.enabled=false
```

  </Tab>
</Tabs>

## Related [#related]

- [Configuring the build plugin](../../setting-up-spring-boot-service/build-plugin/configuration.mdx) — the second half of wiring a service up; required
  for the instance to register.
- [Configuring Master](../../setting-up-master-ui/configuring-master/configuring-master.mdx) — where the signing key, eviction timeout, and
  Kubernetes scan are set.
- [What is Master?](../../setting-up-master-ui/what-is-master.mdx) — background on how Master uses the data the starter
  exposes.
- [Authentication](../../setting-up-master-ui/authentication/authentication.mdx) — deeper look at JWT and authority
  resolution on both sides.
