Setup
BootUI runs on Spring Boot 4 (servlet or WebFlux) and Quarkus, serving the same console and the same /bootui/api/** JSON contract from a shared, framework-neutral engine. The numbered steps below cover the Spring Boot servlet starter; if you are on Spring Boot WebFlux, jump to BootUI on Spring WebFlux; if you are on Quarkus, jump to BootUI on Quarkus.
1) Prerequisites
- Java 17 or later
- Spring Boot 4.x application
- Maven or Gradle (or their local wrappers)
2) Add the starter dependency
The simplest setup is to drop the starter into your build — nothing else is required. BootUI ships dormant and only wakes up in local development (the dev / local profiles, or when spring-boot-devtools is on the classpath), and it force-disables itself in prod / production.
<dependency>
<groupId>com.julien-dubois.bootui</groupId>
<artifactId>bootui-spring-boot-starter</artifactId>
<version>1.12.0</version>
</dependency>
// Groovy DSL (build.gradle)
runtimeOnly 'com.julien-dubois.bootui:bootui-spring-boot-starter:1.12.0'
// Kotlin DSL (build.gradle.kts)
runtimeOnly("com.julien-dubois.bootui:bootui-spring-boot-starter:1.12.0")
Prefer to keep the starter jar out of your production artifact entirely? Use the dev-only profile setup instead.
3) Run your app in development mode
Start the application with the dev profile active so BootUI turns on:
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
./gradlew bootRun --args='--spring.profiles.active=dev'
BootUI also activates automatically when spring-boot-devtools is on the classpath. To force it on or off:
bootui.enabled=AUTO
bootui.enabled=ON
bootui.enabled=OFF
prod and production profiles disable BootUI unless bootui.enabled=ON is set. Invalid bootui.enabled values fail closed and keep BootUI disabled.
YAML users
In application.yml, YAML parses ON/OFF (and yes/no/true/false) as booleans, so bootui.enabled: ON arrives as true. BootUI accepts these: ON/true/yes enable it and OFF/false/no disable it, so the documented bootui.enabled: ON works unquoted in YAML.
4) Open BootUI
Nice job! BootUI is now configured 🚀
Visit: http://localhost:8080/bootui
Advanced: scope BootUI to a dev-only profile
The simple install above leaves the starter jar on the classpath in every build — BootUI just stays disabled outside development. If you would rather keep the starter out of your production build entirely, declare it in a dedicated dev build profile instead. The same profile can switch on the dev Spring Boot profile for you, so a single flag both adds the starter and activates BootUI.
Add a dev Maven profile that declares the starter and tells the Spring Boot plugin to run with the dev Spring Boot profile:
<profiles>
<profile>
<id>dev</id>
<dependencies>
<dependency>
<groupId>com.julien-dubois.bootui</groupId>
<artifactId>bootui-spring-boot-starter</artifactId>
<version>1.12.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<profiles>
<profile>dev</profile>
</profiles>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Add a dev Gradle profile that is activated with the -Pdev project property. It pulls in the starter and sets the dev Spring Boot profile on bootRun:
// Groovy DSL (build.gradle)
if (project.hasProperty('dev')) {
dependencies {
runtimeOnly 'com.julien-dubois.bootui:bootui-spring-boot-starter:1.12.0'
}
tasks.named('bootRun') {
systemProperty 'spring.profiles.active', 'dev'
}
}
// Kotlin DSL (build.gradle.kts)
if (project.hasProperty("dev")) {
dependencies {
"runtimeOnly"("com.julien-dubois.bootui:bootui-spring-boot-starter:1.12.0")
}
tasks.named<org.springframework.boot.gradle.tasks.run.BootRun>("bootRun") {
systemProperty("spring.profiles.active", "dev")
}
}
Then activate the profile when you start the app. This both adds the BootUI starter and turns on the dev Spring Boot profile:
./mvnw spring-boot:run -Pdev
./gradlew bootRun -Pdev
BootUI on Spring WebFlux
BootUI also ships a reactive starter for Spring Boot WebFlux (Netty / DispatcherHandler) applications. It serves the same Vue console at /bootui and the same /bootui/api/** JSON contract as the servlet starter above, backed by the same framework-neutral BootUI engine — only the request/response binding differs underneath.
Prerequisites
- Java 17 or later
- Spring Boot 4.x application configured for WebFlux (
spring-boot-starter-webflux, notspring-boot-starter-web) - Maven or Gradle (or their local wrappers)
Add the reactive starter dependency
Use bootui-spring-boot-starter-reactive instead of bootui-spring-boot-starter. It depends on spring-boot-starter-webflux (not -web), so it will not pull in Tomcat or force a servlet WebApplicationType. The same activation rule applies: BootUI ships dormant and only wakes up in local development.
<dependency>
<groupId>com.julien-dubois.bootui</groupId>
<artifactId>bootui-spring-boot-starter-reactive</artifactId>
<version>1.12.0</version>
</dependency>
// Groovy DSL (build.gradle)
runtimeOnly 'com.julien-dubois.bootui:bootui-spring-boot-starter-reactive:1.12.0'
// Kotlin DSL (build.gradle.kts)
runtimeOnly("com.julien-dubois.bootui:bootui-spring-boot-starter-reactive:1.12.0")
Do not add both bootui-spring-boot-starter and bootui-spring-boot-starter-reactive to the same application — Spring Boot picks one WebApplicationType (servlet or reactive), so only the matching autoconfiguration ever activates.
Run your app in development mode
Same as the servlet starter — start with the dev profile active (or rely on spring-boot-devtools / bootui.enabled=ON):
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
./gradlew bootRun --args='--spring.profiles.active=dev'
Profile activation, not just a default
BootUiActivationCondition checks the active profiles, not spring.profiles.default. If your application.properties only sets a default profile (spring.profiles.default=dev), a bare java -jar launch with no explicit profile leaves BootUI disabled (404 on /bootui) even though spring-boot:run/your IDE's run configuration may set an active profile for you. Pass --spring.profiles.active=dev (or SPRING_PROFILES_ACTIVE=dev) explicitly when running a packaged jar by hand. This applies identically to the servlet starter.
Open BootUI
Nice job! BootUI is now configured 🚀
Visit: http://localhost:8080/bootui
Activation and safety on Spring WebFlux
Activation uses the exact same BootUiActivationCondition as the servlet starter (bootui.enabled=ON|OFF, bootui.enabled-profiles / bootui.disabled-profiles, or spring-boot-devtools on the classpath) — there is no separate reactive-specific flag. The request-time safety model is identical to the servlet starter and to Quarkus: the same shared LocalhostGuard (loopback-source trust, a Host allow-list as a DNS-rebinding defense, and cross-site-write / CSRF protection), ported to a WebFilter instead of a servlet Filter. The same configuration keys apply:
bootui.allow-non-localhost=false # default: reject non-loopback callers
bootui.allowed-hosts=localhost # extra Host header values to accept
bootui.trusted-proxies=172.16.0.0/12 # extra source ranges (e.g. a Docker gateway)
bootui.trust-container-gateway=AUTO # auto-trust the container gateway in dev containers
The Running inside a Docker container guidance below applies unchanged. Per-panel bootui.panels.* enable / read-only toggles and the bootui.read-only master switch are enforced identically as well.
Which panels are available on Spring WebFlux
The large majority of BootUI's panels are live on the reactive adapter, including every advisor scan except Security, plus Flyway/Liquibase, Database Connection Pools, Cache, SQL Trace, Log Tail, Security Logs, Exceptions, and Live Activity (over a rebuilt reactive streaming/capture layer). The following panels are not yet available:
- HTTP Sessions — not applicable: it is the servlet container's
HttpSessionAPI, with no reactive equivalent. - Security (the advisor) and the raw Spring Security panel — not yet ported: both key off the servlet
SecurityFilterChainbean, which a reactive Spring Security setup never registers (it registers aWebFilterChainProxyinstead); aServerHttpSecurity/SecurityWebFilterChainruleset is planned. - REST Client — not yet ported: the standalone panel is only available on the Spring MVC (servlet) adapter.
For the authoritative, per-panel detail and the reasoning behind each gap, see Features and BootUI on Spring WebFlux.
BootUI on Quarkus
BootUI also ships as a Quarkus extension. It serves the same Vue console at /bootui and the same /bootui/api/** JSON contract as the Spring Boot starter, backed by the Quarkus build of the framework-neutral BootUI engine.
Prerequisites
- Java 17 or later
- A Quarkus application (built and tested against the version pinned by the root
pom.xml'squarkus.platform.versionproperty; currently3.37.2) - Maven or Gradle (or their local wrappers)
Add the extension
Add the BootUI Quarkus extension to your build — nothing else is required. BootUI wires itself up only in Quarkus' dev and test launch modes and stays completely dark in production, so it is safe to leave on the classpath.
<dependency>
<groupId>com.julien-dubois.bootui</groupId>
<artifactId>bootui-quarkus</artifactId>
<version>1.12.0</version>
</dependency>
// Groovy DSL (build.gradle)
implementation 'com.julien-dubois.bootui:bootui-quarkus:1.12.0'
// Kotlin DSL (build.gradle.kts)
implementation("com.julien-dubois.bootui:bootui-quarkus:1.12.0")
You only declare bootui-quarkus; the matching bootui-quarkus-deployment artifact is resolved automatically by the Quarkus extension mechanism.
Run your app in development mode
Start Quarkus in dev mode. BootUI activates automatically — there is no profile or flag to set:
./mvnw quarkus:dev
./gradlew quarkusDev
Open BootUI
Nice job! BootUI is now configured 🚀
Visit: http://localhost:8080/bootui
Activation and safety on Quarkus
Activation is governed entirely by the Quarkus launch mode, not by a Spring-style profile or a bootui.enabled flag:
dev(quarkus:dev) andtest(@QuarkusTest) — the console, its/bootui/api/**endpoints, the CDI beans, and the safety filter are all wired up.- Production (
NORMALlaunch mode — a packagedquarkus-run.jaror a native image) — BootUI is not wired at all. The API, beans, and safety filter are absent, so the console has no data to serve. This is fail-closed by design: there is no flag that turns BootUI on in a production build.
The request-time safety model is identical to Spring Boot: BootUI is loopback-only by default and shares the same LocalhostGuard (loopback-source trust, a Host allow-list as a DNS-rebinding defense, and cross-site-write / CSRF protection). Non-loopback API callers must additionally authenticate with the BootUI bearer token. The same opt-in keys apply, read live from MicroProfile Config:
bootui.allow-non-localhost=false # default: reject non-loopback callers
bootui.allowed-hosts=localhost # extra Host header values to accept
bootui.trusted-proxies=172.16.0.0/12 # extra source ranges (e.g. a Docker gateway)
bootui.trust-container-gateway=AUTO # auto-trust the container gateway in dev containers
# bootui.authentication.token=... # optional stable token; otherwise generated at startup
The Running inside a Docker container guidance below applies to Quarkus too — use the same keys (only the Spring-specific activation note differs; on Quarkus, dev mode is already active).
A few capabilities are Spring-only today: runtime configuration overrides (the Configuration panel is read-only on Quarkus — there is no write path yet). Per-panel bootui.panels.* enable / read-only toggles and the bootui.read-only master switch are enforced identically on both frameworks. Everything else behaves the same across both frameworks.
Which panels are available on Quarkus
Most of BootUI's panels are live on Quarkus. A handful target Spring-specific runtime concepts and are clearly marked not applicable on Quarkus — for example GraalVM and CRaC readiness, Conditions, Startup Timeline, HTTP Sessions, Spring Data, Spring Security, and DevTools. (Quarkus builds native images and generates reachability metadata itself, and the others have no Quarkus equivalent.)
For the authoritative, per-panel availability, see Features and the BootUI on Quarkus notes. To try a fully wired Quarkus app, see Try the sample app.
Safety defaults
BootUI is intended for local development only. By default it:
- Activates in
AUTOmode only for thedev/localprofiles or DevTools. - Rejects non-loopback requests.
- Requires bearer-token authentication for non-loopback API requests whenever remote access is explicitly enabled; localhost remains authentication-free.
- Applies one cross-framework security-header policy to the whole
/bootui/**surface, with no-store caching for APIs, streams, and downloads and immutable caching only for successfully served content-hashed assets. - Permits
/bootui/**through Spring Security when Spring Security is present, with a startup warning, so the local console remains directly reachable while the loopback-only filter still applies. - Masks secret-like configuration values.
- Exposes the local Actuator endpoints used by BootUI panels when BootUI is active.
- Captures local application spans for the Traces panel when telemetry and the panel are enabled.
- Disables itself for
prod/productionprofiles. - Stores runtime configuration overrides in
.bootui/application-bootui.properties, not in your source config files.
Every visible panel can be disabled with bootui.panels.<panel-id>.enabled=false. Panels with mutating browser actions can also be made read-only with bootui.panels.<panel-id>.read-only=true, and bootui.read-only=true makes the whole BootUI application read-only. See the property reference for the full panel list.
Runtime overrides
The Configuration panel can create, update, and delete local runtime overrides. Overrides are stored in .bootui/application-bootui.properties by default, loaded at high precedence on the next startup, and never modify your application source configuration. Already-bound @ConfigurationProperties beans may keep their previous value until the app restarts; BootUI returns that warning with every override mutation.
Command-line (non-web) applications
BootUI also works in non-web applications, such as command-line apps. The starter brings Spring MVC and an embedded servlet container, so when BootUI is active it automatically starts a servlet web server even if your application is configured as non-web (spring.main.web-application-type=none or SpringApplication#setWebApplicationType(NONE)). Your CommandLineRunner / ApplicationRunner beans still run as usual; the application simply keeps running so the console stays reachable.
Because BootUI only activates in development contexts by default, this never affects production. Applications that are already servlet web apps, or that are explicitly configured as reactive, are left untouched. To opt out and keep your application's web-application type exactly as declared, set bootui.force-web=false.
BootUI never forces the web type on Spring Cloud's transient bootstrap application context (the early, non-web context created by spring-cloud-starter-bootstrap for Spring Cloud Config). That context has no embedded web server, so forcing it would crash startup with MissingWebServerFactoryBeanException; BootUI detects it and leaves it alone, then forces the servlet web type on your main application as usual.
Running inside a Docker container
BootUI works when your application runs inside a container, but its loopback-only safety filter needs a small opt-in first. When you publish a port (for example docker run -p 8080:8080 …) and browse to http://localhost:8080/bootui, the request reaches the application from the Docker gateway (a non-loopback address), so BootUI rejects it by default — it fails closed for non-loopback callers. The gateway address depends on the Docker flavor:
- Linux Docker Engine uses the default bridge gateway, typically
172.17.0.1(inside172.16.0.0/12). - Docker Desktop (macOS and Windows) routes published-port traffic through its gateway VM, so the request arrives from
192.168.65.1(inside192.168.65.0/24). This is the address you will see in aLocalhostOnlyFilterrejection log line such asBootUI rejected non-loopback request from 192.168.65.1 to /bootui/api/health.
Check your own setup with docker network inspect bridge (look at IPAM.Config.Gateway) or the source address in the BootUI rejection log line, and trust that range.
Two things have to be in place:
- Activate BootUI inside the container. A repackaged jar strips DevTools, and activation checks the active profiles (not
spring.profiles.default), so set one explicitly —SPRING_PROFILES_ACTIVE=devorBOOTUI_ENABLED=ON. Without this you get a404on/bootui, not a rejection. - Trust the container gateway. The simplest opt-in is
bootui.trust-container-gateway=AUTO: while running inside a container BootUI auto-detects the gateway address(es) that published-port traffic arrives from and trusts just those/32(or/128) hosts as loopback-equivalent — no need to know the gateway IP or subnet, on any Docker flavor. Detection covers both runtimes: on Linux Docker Engine it reads the bridge default gateway from/proc/net/route(the SNAT source, e.g.172.17.0.1); on Docker Desktop (macOS/Windows) the SNAT source (192.168.65.1) is not the route-table gateway, so BootUI resolves thegateway.docker.internalDNS name that Docker Desktop injects into every container. This relaxes only the source-address check; theHostallow-list (DNS-rebinding defense) and cross-site write (CSRF) protection stay in force, and sibling containers are not trusted (their traffic carries their own IP, not the gateway). The lookup is resolved once and cached, and fails closed: on Linux Docker Engine and bare metalgateway.docker.internaldoes not resolve, which simply means "no extra gateway" (the route-table detection still applies). On Docker Desktop the Docker-Desktop branch therefore relies on Docker's embedded DNS resolvinggateway.docker.internal; if that name is unavailable (for example you have disabled it), setbootui.trusted-proxies=192.168.65.0/24instead.
docker run -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=dev \
-e BOOTUI_TRUST_CONTAINER_GATEWAY=AUTO \
your-image
Then open http://localhost:8080/bootui from the host. Use ON instead of AUTO to trust a detected gateway even when the container heuristics are inconclusive.
Security caveat — published-port bind address.
-p 8080:8080binds0.0.0.0:8080on the host, so a remote LAN client hittinghostLanIP:8080is also SNAT'd to the same gateway. Trusting the gateway/32therefore trusts "anything that can reach the published port", which in this bind mode includes the LAN — not strictly loopback. This is acceptable for a dev tool (BootUI is dev/local-gated and the Host + CSRF defenses remain in force) and is why the feature is off by default. For strict loopback equivalence, bind the port to localhost only:docker run -p 127.0.0.1:8080:8080 ….
Custom proxies, bridges, or LAN setups
If you front the app with a reverse proxy, use a custom Docker network, or otherwise reach BootUI from a source other than the auto-detected gateway, use bootui.trusted-proxies instead. It trusts additional source IP ranges (CIDR notation) while keeping the same Host and CSRF defenses — pick the range that matches your Docker flavor:
# Linux Docker Engine: the default bridge gateway 172.17.x lives inside 172.16.0.0/12
bootui.trusted-proxies=172.16.0.0/12
# Docker Desktop (macOS/Windows): the gateway is 192.168.65.1, so trust 192.168.65.0/24 instead
#bootui.trusted-proxies=192.168.65.0/24
# Accept the hostname you browse with (localhost is already a built-in loopback name)
bootui.allowed-hosts=localhost
Or as environment variables on the container:
docker run -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=dev \
-e BOOTUI_TRUSTED_PROXIES=172.16.0.0/12 \
your-image
On Docker Desktop, use -e BOOTUI_TRUSTED_PROXIES=192.168.65.0/24 instead.
Scope bootui.trusted-proxies as narrowly as you can: for a user-defined Docker network, prefer that network's specific subnet over the broad 172.16.0.0/12, and keep it limited to trusted local/dev networks. Reserve bootui.allow-non-localhost=true as a blunt last resort.
Troubleshooting
| Symptom | Check |
|---|---|
/bootui returns 404 | Use the dev or local profile, add DevTools, or set bootui.enabled=ON. In application.yml, bootui.enabled: ON is valid — YAML parses it as a boolean, which BootUI accepts as ON. |
BootUI is disabled in prod | This is intentional; only bootui.enabled=ON can force activation with a disabled profile. |
| Command-line app now stays up | Expected: BootUI starts a servlet server so the console is reachable. Set bootui.force-web=false to keep the app non-web. |
| Browser is rejected | BootUI accepts loopback callers and fails closed for everything else. Inside a container, set bootui.trust-container-gateway=AUTO to auto-detect and trust the default gateway /32 (the SNAT source of published-port traffic) — no subnet needed on any Docker flavor, and the Host + CSRF protections stay on. For a custom proxy/bridge or LAN access, add that source range to bootui.trusted-proxies instead — 172.16.0.0/12 on Linux Docker Engine, 192.168.65.0/24 on Docker Desktop (macOS/Windows) — plus the hostname you browse with to bootui.allowed-hosts. Use bootui.allow-non-localhost=true only as a blunt last resort on a trusted local network. |
| Spring Security blocks UI | BootUI auto-registers a /bootui/** permit-all chain when Spring Security is active; check for a custom higher-priority chain. |
localhost redirected you too many times | BootUI serves the console at both /bootui and /bootui/ with no redirect, so a host trailing-slash–stripping filter or proxy (e.g. Spring's UrlHandlerFilter.trailingSlashHandler("/**").wrapRequest(), a standard Boot 4 idiom) can't loop on it. If you still hit this on an older BootUI, upgrade or open /bootui/ (with the trailing slash) directly. |
| A panel is empty | Enable the relevant Actuator endpoint or optional Spring module; BootUI degrades to stable empty DTOs when data is unavailable. |
| Startup Timeline is empty | Leave bootui.startup.enabled=true and bootui.startup.capacity greater than zero, or provide your own BufferingApplicationStartup. |
| Secrets are hidden | Default exposure is MASKED; use METADATA_ONLY to hide all values or FULL only in trusted local sessions. |
| Static resources disabled | spring.web.resources.add-mappings=false is bypassed by BootUI: it registers its own /bootui/** handler for its Web-based dashboard assets and logs a WARN line; the host's other static resources stay disabled. |