A startup banner is decoration. I expected this one to be a five-minute configuration change.
Then I changed the text on the packaged application:
QUARKUS_BANNER_GENERATOR_TEXT=RUNTIME \
java -jar target/quarkus-app/quarkus-run.jarThe application ignored me and printed THE MAIN THREAD again.
My first thought was that I had written the environment variable incorrectly. Its name was correct, but it arrived too late. Quarkus had already rendered the text during the build and stored the finished banner in the application.
Color behaves differently. The same packaged application can print a colored banner in my terminal and a plain one in a log file. So this small extension gives us a clear example of a Quarkus build-time decision and a runtime decision in the same feature.
What we are building
We will create a small Quarkus REST application with this two-line startup banner:
________ ________
/_ __/ / / / ____/
/ / / /_/ / __/
/ / / __ / /___
/_/ /_/ /_/_____/
__ ______ _____ __ ________ ______ _________ ____
/ |/ / | / _/ | / / /_ __/ / / / __ \/ ____/ | / __ \
/ /|_/ / /| | / // |/ / / / / /_/ / /_/ / __/ / /| | / / / /
/ / / / ___ |_/ // /| / / / / __ / _, _/ /___/ ___ |/ /_/ /
/_/ /_/_/ |_/___/_/ |_/ /_/ /_/ /_/_/ |_/_____/_/ |_/_____/
THE and THREAD use the purple from The Main Thread header. MAIN uses the pink. The terminal background is black.
We will package the application and try to replace the text at runtime. After that, we will switch off ANSI color and check the captured log. One bad font name will also show us where validation happens.
The example uses Quarkus 3.39.1, Java 25, and Quarkus Banner 1.6.0. You need a matching JDK and the Quarkus CLI. You also need curl and rg from ripgrep. Plan about 20 minutes.
Create the application
Create an empty REST application or start from my example in Github:
quarkus create app com.themainthread:quarkus-banner-build-time \
-P io.quarkus.platform:quarkus-bom:3.39.1 \
--java=25 \
--no-code \
--extensions=rest-jackson
cd quarkus-banner-build-timeNow add the Quarkus Banner extension to the dependencies in pom.xml:
<dependency>
<groupId>io.quarkiverse.banner</groupId>
<artifactId>quarkus-banner</artifactId>
<version>1.6.0</version>
</dependency>Quarkus Banner is outside the Quarkus platform BOM, so we pin version 1.6.0 in the dependency. quarkus-rest-jackson gives us a small endpoint to call after startup. Without that endpoint, we would have ASCII art with a Maven build. Nice, but still a little thin.
Create src/main/java/com/themainthread/banner/ThreadResource.java:
package com.themainthread.banner;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/thread")
@Produces(APPLICATION_JSON)
public class ThreadResource {
@GET
public ThreadStatus status() {
return new ThreadStatus(
"The Main Thread",
"Because modern Java deserves better content.");
}
public record ThreadStatus(String name, String mission) {
}
}The Java class has no banner code. It only returns the identity we use in the startup text. The extension gets its input from configuration.
Turn the header into terminal text
Add this complete configuration to src/main/resources/application.properties:
quarkus.application.name=the-main-thread
quarkus.banner-generator.text={#3A338F}THE\n{#CC2266}MAIN {#3A338F}THREAD
quarkus.banner-generator.font=slant
quarkus.banner-generator.background-color=black
quarkus.banner-generator.alignment=center
quarkus.banner-generator.line-spacing=1
%dev.quarkus.console.basic=trueI took the colors from the logo header image. Purple is #3A338F, and pink is #CC2266. Each color marker applies to the characters after it.
The literal \n starts the second FIGlet line. This gives us control over the line break. Terminal wrapping would depend on the available width, which is a poor way to place a name.
slant is one of the bundled FIGlet fonts. Center alignment gives both lines a common width. The extra line spacing keeps THE away from MAIN THREAD. I also use a black background to match the header.
The %dev.quarkus.console.basic=true setting keeps the development console simple. A large banner already uses enough screen space.
Start dev mode:
./mvnw quarkus:devThe terminal should show THE and THREAD in purple and MAIN in pink. The normal Quarkus line follows the banner:
Powered by Quarkus 3.39.1Open another terminal and call the endpoint:
curl -s http://localhost:8080/threadYou should get this response:
{"name":"The Main Thread","mission":"Because modern Java deserves better content."}While dev mode is running, open http://localhost:8080/q/dev-ui/. The Banner card can preview text and fonts. It uses the same renderer as the build, and it can print a preview into the application log.
I prefer the Dev UI for choosing a font. Some font names give you a clear banner. Others give you 180 columns of punctuation. Play around with this as you like.
Follow the banner through augmentation
The properties look like normal Quarkus configuration. The difference is when Quarkus reads them. The extension declares BannerConfig with ConfigPhase.BUILD_TIME in the BannerConfig source.
Build-time configuration includes the text and font. It also includes the colors, alignment, and line spacing. Quarkus reads these values during augmentation. Augmentation is the build phase in which Quarkus extensions inspect the application and generate what the application needs later.
The banner follows this path:
During augmentation, BannerProcessor renders two strings. One contains ANSI escape sequences. The other contains plain text. The processor puts both strings into a GeneratedBannerBuildItem and validates the font while Maven can still stop the build.
At runtime init, BannerRecorder installs a console formatter. It selects one of the two strings based on the console settings and terminal support. FIGlet does not run again.
I like this split because I can point to the exact boundary. Text rendering and validation happen once during the build. Runtime makes only the choice that depends on the current terminal: colored or plain output.
Prove that the text is fixed during the build
Stop dev mode with Ctrl+C, then package the application:
./mvnw packageNow replace the configured text through an environment variable:
QUARKUS_BANNER_GENERATOR_TEXT=RUNTIME \
java -jar target/quarkus-app/quarkus-run.jarThe process still prints THE MAIN THREAD. QUARKUS_BANNER_GENERATOR_TEXT is the correct environment-variable form of the property. The variable arrives after augmentation, so it cannot change the rendered strings inside the packaged application.
This is easy to miss in a deployment pipeline. Many teams build one artifact and promote it through test, staging, and production. The banner should contain a stable application or product name in that setup. An environment name would stay fixed to the value present during the build, even when the artifact moves to another environment.
Stop the process with Ctrl+C before the next check.
Let the terminal choose color
The text is fixed, but color still depends on the terminal. Run the packaged application with the NO_COLOR convention enabled:
NO_COLOR=1 java -jar target/quarkus-app/quarkus-run.jarThe same ASCII text appears without the purple, pink, or black ANSI styling. Stop the process with Ctrl+C.
We can also check the output as bytes. Disable Quarkus console color and capture the log:
java -Dquarkus.console.color=false \
-jar target/quarkus-app/quarkus-run.jar \
> target/no-color.log 2>&1Wait for the startup line, then press Ctrl+C. Search the file for the ANSI escape byte:
rg --pcre2 '\x1b' target/no-color.log
echo $?rg prints nothing and exits with status 1. It found no escape sequences. The ASCII banner is still present in target/no-color.log.
The recorder also checks terminal support when quarkus.console.color has no explicit value. An interactive color terminal gets the ANSI string. A dumb terminal or a process with NO_COLOR gets the plain string. Both strings came from the same build.
A bad font stops the build
Font validation gives us another direct test. Ask for a font that does not exist:
./mvnw package -DskipTests \
-Dquarkus.banner-generator.font=definitely-not-a-fontMaven stops during configuration validation:
Unknown banner font 'definitely-not-a-font'. It must be one of the fonts
bundled with the extension (see the list in FIGLET-FONTS.md).You can use the extension’s bundled font list or the Dev UI preview to choose a valid name.
Build once more without the invalid override:
./mvnw packageWhat I would put in a service banner
I would keep the text stable and short. A product name or service name fits well because it identifies the same artifact in every environment. Very long names become hard to read with wide FIGlet fonts, especially in CI logs.
Secrets and hostnames obviously should stay out of the banner. The text is built into the artifact and appears in logs. Dynamic deployment data belongs in structured startup logging, where a log collector can parse it.
I also check plain output before release. Production logs often pass through collectors that have no use for ANSI codes. The words need to make sense without color, because color alone is a poor signal for accessibility.
The extension bundles fonts under their original licenses. Check the notes in the extension documentation if you plan to redistribute a font beyond normal application output.
The banner makes the build boundary visible
The packaged application contains two rendered strings. Augmentation created and validated them. Runtime init picks the string that fits the console, and startup performs no font rendering.
I enjoy this extension because the result is visible. We can see where build-time configuration ends and runtime behavior begins. The same boundary exists in larger Quarkus extensions, but ASCII text makes it much easier to follow.



