Config
This guide describes how to create a sample Helidon SE project that can be used to run some basic examples using both default and custom configuration.
What you need
For this 20 minute tutorial, you will need the following:
| Requirement | Description |
|---|---|
| Java 21 (Open JDK 21) | Helidon requires Java 21+ (25+ recommended). |
| Maven 3.8+ | Helidon requires Maven 3.8+. |
| Docker 18.09+ | If you want to build and run Docker containers. |
| Kubectl 1.16.5+ | If you want to deploy to Kubernetes, you need kubectl and a Kubernetes cluster. |
Prerequisite product versions for Helidon 4.4.0-SNAPSHOT
Verify Prerequisites:
java -version
mvn --version
docker --version
kubectl version
Setting JAVA_HOME:
# On Mac
export JAVA_HOME=`/usr/libexec/java_home -v 21`
# On Linux
# Use the appropriate path to your JDK
export JAVA_HOME=/usr/lib/jvm/jdk-21
Getting Started with Configuration
Helidon provides a very flexible and comprehensive configuration system, offering you many application configuration choices. You can include configuration data from a variety of sources using different formats, like JSON and YAML. Furthermore, you can customize the precedence of sources and make them optional or mandatory. This guide introduces Helidon SE configuration and demonstrates the fundamental concepts using several examples. Refer to Helidon Config for the full configuration concepts documentation.
Create a Sample Helidon SE Project
Use the Helidon SE Maven archetype to create a simple project that can be used for the examples in this guide.
Run the Maven archetype:
mvn -U archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion=4.4.0-SNAPSHOT \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.se
The project will be built and run from the helidon-quickstart-se directory:
cd helidon-quickstart-se
Configuration Formats
Helidon configuration sources can use different formats for the configuration data. You can specify the format on a per-source basis, mixing and matching formats as required. Here are the supported formats, each with the extension name you should use. By default, Helidon will determine the media type based on the extension name.
- Java Property (.properties)
- JSON (.json)
- YAML (.yaml)
- HOCON (.conf)
The remainder of this document will use these formats in examples and show you how to configure Helidon to parse them.
Default Configuration
Helidon has an internal configuration, so you are not required to provide any
configuration data for your application, though in practice you most likely
would. By default, that configuration can be overridden from three sources:
system properties, environment variables, and the contents of application.yaml
in the classpath. For example, if you specify a custom server port in
application.yaml then your server will listen on that port.
In your application code, Helidon uses the default configuration when you create
a default Config object. See the following code from the project you created.
View Main#main:
Source Precedence for Default Configuration
In order to properly configure your application using configuration sources, you need to understand the precedence rules that Helidon uses to merge your configuration data. By default, Helidon will use the following sources in precedence order:
- Java system properties
- Environment variables
- Configuration specified in
application.yaml
If any of the Helidon required properties are not specified in one of these
source, like server.port, then Helidon will use a default value.
The following examples will demonstrate the default precedence order.
Default Configuration Resource
Change a configuration parameter in the default configuration resource file,
application.yaml. There are no environment variable or system property
overrides defined.
Change app.greeting in resources/application.yaml as follows:
app:
greeting: HelloFrom-application.yaml
Build the application, skipping unit tests, then run it:
mvn package -DskipTests=true
java -jar target/helidon-quickstart-se.jar
Run the curl command in a new terminal window and check the response:
curl http://localhost:8080/greet
The new app.greeting value in application.yaml is used.
Environment Variable Override
An environment property has a higher precedence than application.yaml.
Set the environment variable and restart the application:
export APP_GREETING=HelloFromEnvironment
java -jar target/helidon-quickstart-se.jar
Invoke the endpoint below and check the response:
curl http://localhost:8080/greet
The environment property took precedence over application.yaml.
{
"message": "HelloFromEnvironment World!"
}
System Property Override
A system variable has a higher precedence than the environment property.
Restart the application with a system property. The APP_GREETING environment variable is still set:
export APP_GREETING=HelloFromEnvironment
java -Dapp.greeting="HelloFromSystemProperty" -jar target/helidon-quickstart-se.jar
Invoke the endpoint below and check the response:
curl http://localhost:8080/greet
The system variable app.greeting took precedence over the environment property
and the value in application.yaml.
{
"message": "HelloFromSystemProperty World!"
}
Custom Configuration Sources
To use custom configuration sources, your application needs to specify the
sources when it creates Config object. By doing this, you are in full control
of all configuration sources and precedence. By default, the environment
variable and system property sources are enabled, but you can disable them using
the disableEnvironmentVariablesSource and disableSystemPropertiesSource
methods.
This section will show you how to use a custom configuration with various sources, formats, and precedence rules.
Full List of Configuration Sources
Here is the full list of external config sources that you can use programmatically.
- Environment variables - the property is a name/value pair.
- Java system properties - the property is a name/value pair.
- Resources in the classpath - the contents of the resource is parsed according to its inferred format.
- File - the contents of the file is parsed according to its inferred format.
- Directory - each non-directory file in the directory becomes a config entry: the file name is the key. and the contents of that file are used as the corresponding config String value.
- A URL resource - contents is parsed according to its inferred format.
You can also define custom sources, such as Git, and use them in your Helidon application. See Advanced Config for more information.
Classpath Sources
The first custom resource example demonstrates how to add a second internal
configuration resource that is discovered in the classpath. The code needs to
build a Config object, which in turn is used to build the Server object. The
Config object can be built using a Config.Builder, which lets you inject any
number of sources into the builder. Furthermore, you can set the order of
precedence for the sources. The first source has the highest importance, then
the next has second highest, and so forth.
Add a resource file, named config.properties to the resources directory with
the following contents:
app.greeting=HelloFrom-config.properties
Update the Main class, Replace the Config.create() call with buildConfig(), and add buildConfig method:
Build and run the application (without the system property). Invoke the endpoint:
curl http://localhost:8080/greet
Swap the source order and run the test again.
Update the Main class and replace the buildConfig method:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
External File Sources
You can move all or part of your configuration to external files, making them
optional or mandatory. The obvious advantage to this approach is that you do not
need to rebuild your application to change configuration. In the following
example, the app.greeting configuration property will be added to
config-file.properties.
Unset the environment variable so that disableEnvironmentVariablesSource doesn’t need to be called:
unset APP_GREETING
Create a file named config-file.properties in the helidon-quickstart-se directory with the following contents:
app.greeting=HelloFrom-config-file.properties
Update the Main class and replace the buildConfig method:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
optional
method with sources, otherwise Helidon will generate an error during startup
as shown below. This is true for both file and classpath sources. By
default, these sources are mandatory.Update the Main class and replace the buildConfig method:
Build then start the application and you will see the following output:
Exception in thread "main" io.helidon.config.ConfigException: Cannot load data from mandatory source FileConfig[missing-file]. File `missing-file` not found.
To fix this, use the optional method as shown below, then rerun the test.
Directory Source
A directory source treats every file in the directory as a key, and the file contents as the value. The following example includes a directory source as highest precedence.
Create a new directory helidon-quickstart-se/conf then create a file named
app.greeting in that directory with the following contents:
HelloFromFileInDirectoryConf
Update the Main class and replace the buildConfig method:
Build and run the application, then invoke the endpoint and check the response:
curl http://localhost:8080/greet
Exceeding Three Sources
If you have more than three sources, you can use the addSource method as shown
below.
Update the Main class and replace the buildConfig method:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
{
"message": "HelloFromFileInDirectoryConf World!"
}
Configuration Profiles
Instead of directly specifying the configuration sources in your code, you can use a profile file that declares the configuration sources and their attributes.
Simplest way to use a profile is to define a config-profile.yaml (and possible
other files, such as config-profile-dev.yaml for dev profile) on classpath
or on file system, and create config using Config.create(). The profile can be
changed by a system property config.profile, or using an environment variable
HELIDON_CONFIG_PROFILE.
Profile file can use any supported format, following example is using YAML.
Create a file named config-profile.yaml in the helidon-quickstart-se directory
with the following contents:
Update the Main class and replace the buildConfig method:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
The source precedence order in a profile file is the order of appearance in the
file. This is demonstrated below where the config-file.properties has the
highest order of precedence.
Replace the contents of the config-profile.yaml file:
Restart the application, then invoke the endpoint:
curl http://localhost:8080/greet
When using a profile file, you need to explicitly include both environment variables and system properties as a source if you want to use them.
Replace the contents of the config-profile.yaml file:
You can re-run the previous tests that exercised environment variables and system properties. Swap the two types to see the precedence change. Be sure to unset APP_GREETING after you finish testing.
Accessing Config within an Application
You have used Helidon to customize configuration behavior from your code using
the Config and Config.Builder classes. As discussed previously, Helidon
reads configuration from a config source, which uses a config parser to
translate the source into an in-memory tree which represents the configuration’s
structure and values. Helidon offers a variety of methods to access in-memory
configuration. These can be categorized as key access or tree navigation.
You have been using key access for all the examples to this point. For example
app.greeting is accessing the greeting child node of the app parent node.
There are many options for accessing this data using navigation methods as
described in Hierarchical Config and Advanced
Config>.
Accessing Config Using Keys or Navigation
The simplest way to access configuration data is using a key, as shown below in
the GreetFeature class. The key can be composite as shown below:
View the GreetService constructor:
You can also access the same greeting by navigating the nodes.
Replace the GreetService constructor with the following code:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
{
"message": "HelloFrom-application.yaml World!"
}
Using Filters and Collections
The Helidon Config class provides several methods that allow you to filter and
customize the traversal of the configuration tree. The example below shows how
to get the greeting node when you only know it is somewhere in the app
subtree.
Replace the contents of the config-profile.yaml file:
sources:
- type: "classpath"
properties:
resource: "application.yaml"
Replace the app section of the application.yaml resource file:
app:
child1: child1-node
child2:
child2a:
greeting: HelloFrom-application.yaml under child2a
child3: child3-node
Update the GreetService.java file and replace the GreetService constructor
with the following:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
{
"message": "HelloFrom-application.yaml under child2a World!"
}
Reacting to Configuration Updates
Even though in-memory config trees are immutable, the config system internally records configuration source metadata that allows it to watch sources for changes. Your application listens for updates to the underlying config sources and reacts to the changes. See Config Mutability Support for a full discussion on this topic. The following example demonstrates how to listen and react to configuration changes.
Replace the contents of the config-profile.yaml file:
sources:
- type: "file"
properties:
path: "./config-file.properties"
change-watcher:
type: "file"
- type: "classpath"
properties:
resource: "application.yaml"
Update the GreetService class and replace the GreetService constructor:
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
{
"message": "HelloFrom-config-file.properties World!"
}
Update config-file.properties with the following contents:
app.greeting=Updated HelloFrom-config-file.properties
After a few seconds, check the response:
curl http://localhost:8080/greet
Integration with Kubernetes
The following example uses a Kubernetes ConfigMap to pass the configuration data
to your Helidon application deployed to Kubernetes. When the pod is created,
Kubernetes will automatically create a local file within the container that has
the contents of the configuration file used for the ConfigMap. This example will
create the file at /etc/config/config-file.properties.
Replace the app section of the application.yaml resource file:
app:
greeting: "Hello"
Update the Main class and replace the buildConfig method:
Replace the GreetService constructor with the following code:
greeting.set(Config.global().get("app.greeting").asString().orElse("Ciao"));
Build and run the application, then invoke the endpoint:
curl http://localhost:8080/greet
Stop the application and build the docker image:
docker build -t helidon-config-se .
Generate a ConfigMap from config-file.properties:
kubectl create configmap helidon-configmap --from-file config-file.properties
View the contents of the ConfigMap:
kubectl get configmap helidon-configmap -o yaml
Output (partial):
Create the Kubernetes YAML specification, named k8s-config.yaml, with the
following contents:
Create and deploy the application into Kubernetes:
kubectl apply -f ./k8s-config.yaml
Get the service information:
kubectl get service/helidon-config
Verify the configuration endpoint using port 31143, your port will likely be different:
curl http://localhost:31143/greet
You can now delete the Kubernetes resources that were just created during this example.
Delete the Kubernetes resources:
kubectl delete -f ./k8s-config.yaml
kubectl delete configmap helidon-configmap
Summary
This guide has demonstrated how to use basic Helidon configuration features. The full configuration documentation, starting with the introduction section at Helidon Config has much more information including the following:
- Architecture
- Parsers
- Extensions
- Filters
- Hierarchical Access
- Property Mapping
- Mutability Support
- and more...
Refer to the following references for additional information: