Skip to main content

Managing Configurations

WSO2 Integrator uses Ballerina's configuration system to externalize runtime settings. This page covers strategies for managing configurations across development, testing, staging, and production environments.

Configuration sources

Ballerina resolves configuration values from multiple sources, with the following precedence (highest first):

PrioritySourceExample
1Environment variablesBAL_CONFIG_VAR_PORT=9090
2Command-line arguments-Cport=9090
3BAL_CONFIG_FILESBAL_CONFIG_FILES=prod.toml
4BAL_CONFIG_DATABAL_CONFIG_DATA='port=9090'
5Config.toml (default file)port = 9090
6Default values in codeconfigurable int port = 8080;

When both BAL_CONFIG_FILES and BAL_CONFIG_DATA are set, BAL_CONFIG_FILES takes precedence. The default Config.toml in the current directory is only used when neither BAL_CONFIG_FILES nor BAL_CONFIG_DATA is specified.

Defining configurable values

Declare configurable variables in your Ballerina code:

configurable int port = 8080;
configurable string dbHost = "localhost";
configurable string dbName = "orders";
configurable string dbUser = ?; // Required -- no default
configurable string dbPassword = ?; // Required -- no default

Per-Environment config files

Directory structure

Organize configuration files by environment:

my-integration/
src/
main.bal
Ballerina.toml
Config.toml # Default / local development
config/
dev/
Config.toml # Development environment
test/
Config.toml # Test environment
staging/
Config.toml # Staging environment
prod/
Config.toml # Production environment

Development config

config/dev/Config.toml:

port = 9090

[db]
host = "dev-db.internal.example.com"
name = "orders_dev"
user = "dev_user"
password = "dev_password"

[logging]
level = "DEBUG"

Production config

config/prod/Config.toml:

port = 9090

[db]
host = "prod-db.internal.example.com"
name = "orders"
user = "svc_orders"
password = "encrypted:xxxxxx"

[logging]
level = "INFO"

Selecting the config file at runtime

Use the BAL_CONFIG_FILES environment variable to specify one or more configuration files:

Linux/macOS (colon-separated):

# Single config file
export BAL_CONFIG_FILES=config/prod/Config.toml
bal run

# Multiple config files (merged in order, later files override earlier)
export BAL_CONFIG_FILES=config/base/Config.toml:config/prod/Config.toml
java -jar my_integration.jar

Windows (semicolon-separated):

REM Single config file
set BAL_CONFIG_FILES=config\prod\Config.toml
bal run

REM Multiple config files
set BAL_CONFIG_FILES=config\base\Config.toml;config\prod\Config.toml
java -jar my_integration.jar
note

Once BAL_CONFIG_FILES is set, the default Config.toml in the current directory is ignored unless explicitly included in the path.

Using BAL_CONFIG_DATA

Provide TOML configuration directly as an environment variable:

export BAL_CONFIG_DATA='port=9090
dbHost="prod-db.example.com"
[logging]
level="INFO"'

java -jar my_integration.jar

This is useful in containerized environments where mounting config files is not convenient.

Command-line arguments

Override specific configuration values using the -C flag:

# Single value
bal run -- -Cport=9090

# Multiple values
bal run -- -Cport=9090 -CdbHost=localhost -CdbName=orders

# For JAR files
java -jar my_integration.jar -Cport=9090 -CdbPassword=secret

Command-line arguments have higher priority than TOML files but lower priority than environment variables.

Environment variable overrides

Override any configurable value using environment variables with the naming convention:

BAL_CONFIG_VAR_<VARIABLE_NAME>

Key rules:

  • All characters must be UPPERCASE
  • Variable names from code are uppercased (e.g., portPORT, dbHostDBHOST)
  • Underscores in variable names are preserved (e.g., db_hostDB_HOST)

Platform-specific syntax

Linux/macOS:

export BAL_CONFIG_VAR_PORT=9090
bal run

Windows:

set BAL_CONFIG_VAR_PORT=9090
bal run

Examples by data type

configurable int port = 8080;
configurable boolean isAdmin = false;
configurable float maxPayload = 1.0;
configurable string dbPassword = ?;
export BAL_CONFIG_VAR_PORT=9090
export BAL_CONFIG_VAR_ISADMIN=true
export BAL_CONFIG_VAR_MAXPAYLOAD=2.5
export BAL_CONFIG_VAR_DBPASSWORD="s3cur3p@ss"
java -jar my_integration.jar

Module-level configurables

For configurables defined in separate modules, the environment variable includes the full module path. Given a project structure:

my_integration/
├── main.bal
└── modules/
└── database/
└── database.bal

If modules/database/database.bal contains:

configurable string db_host = "localhost";
configurable int db_port = 5432;

And your Config.toml organizes them as:

[my_integration.database]
db_host = "prod-db.example.com"
db_port = 3306

The environment variables are:

export BAL_CONFIG_VAR_MY_INTEGRATION_DATABASE_DB_HOST="env-db.example.com"
export BAL_CONFIG_VAR_MY_INTEGRATION_DATABASE_DB_PORT=5432
java -jar my_integration.jar

Pattern: BAL_CONFIG_VAR_<PACKAGE>_<MODULE>_<VARIABLE> with all dots converted to underscores and all characters uppercase.

Supported types and limitations

note

Supported types for environment variables:

  • Simple types: int, byte, float, boolean, string, decimal, enum, xml
  • Values must be in the toString() representation of the type

Not supported:

  • Record types
  • Array types
  • Map types
  • Complex structured data

For structured configuration using TOML sections with record types, use BAL_CONFIG_FILES or BAL_CONFIG_DATA instead of individual environment variables.

Configuration precedence in practice

Example demonstrating the complete precedence order:

configurable int port = 8080;  // Priority 6: Default value
# Config.toml (Priority 5: Default file)
port = 9090
# Priority 4: BAL_CONFIG_DATA
export BAL_CONFIG_DATA='port=7777'

# Priority 3: BAL_CONFIG_FILES (overrides BAL_CONFIG_DATA)
export BAL_CONFIG_FILES=custom.toml # contains port=6666

# Priority 2: Command-line (overrides BAL_CONFIG_FILES)
java -jar app.jar -Cport=5555

# Priority 1: Environment variable (HIGHEST - overrides everything)
export BAL_CONFIG_VAR_PORT=4444

Result: port = 4444 (environment variable wins)

Precedence cascade:

  • If BAL_CONFIG_VAR_PORT not set → uses command-line value 5555
  • If no command-line → uses BAL_CONFIG_FILES value 6666
  • If no BAL_CONFIG_FILES → uses BAL_CONFIG_DATA value 7777
  • If no BAL_CONFIG_DATA → uses Config.toml value 9090
  • If no Config.toml → uses default value 8080

Kubernetes ConfigMaps and secrets

ConfigMap for Non-Sensitive values

apiVersion: v1
kind: ConfigMap
metadata:
name: integration-config
data:
Config.toml: |
port = 9090

[db]
host = "db.internal.svc.cluster.local"
name = "orders"
user = "svc_orders"

[logging]
level = "INFO"

Secret for sensitive values

apiVersion: v1
kind: Secret
metadata:
name: integration-secrets
type: Opaque
stringData:
BAL_CONFIG_VAR_DBPASSWORD: "s3cur3p@ss"
BAL_CONFIG_VAR_APIKEY: "ak_xxxxxxxxxxxxx"

For module-level variables like [database].password, use the full path:

stringData:
BAL_CONFIG_VAR_MY_INTEGRATION_DATABASE_PASSWORD: "s3cur3p@ss"

Mount in deployment

containers:
- name: integration
image: myorg/my-integration:1.0.0
env:
- name: BAL_CONFIG_FILES
value: /config/Config.toml
envFrom:
- secretRef:
name: integration-secrets
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
configMap:
name: integration-config

Consolidated Multi-Service configuration

When running multiple integrations in a consolidated package, namespace each service's config:

# Shared settings
[http]
maxConnections = 100

# Order Service
[order_service.db]
host = "db.internal.example.com"
name = "orders"

[order_service.http]
port = 9091

# Inventory Service
[inventory_service.db]
host = "db.internal.example.com"
name = "inventory"

[inventory_service.http]
port = 9092

# Notification Service
[notification_service.smtp]
host = "smtp.example.com"
port = 587

Configuration validation

Ballerina validates configurations at startup. If a required configurable has no value, the application fails to start with a clear error:

error: value not provided for required configurable variable 'dbPassword'

Use constrained types to enforce valid values:

configurable int port = 8080;    // Must be an integer
configurable string dbHost = ?; // Required string
configurable "DEBUG"|"INFO"|"WARN"|"ERROR" logLevel = "INFO"; // Enum constraint

Best practices

PracticeDescription
Never commit secretsKeep Config.toml with secrets out of version control
Use .gitignoreAdd config/prod/Config.toml to .gitignore
Validate earlyUse required configurables (= ?) for mandatory settings
Layer configsUse base + environment-specific files for DRY configuration
Prefer env vars for secretsUse environment variables for passwords and API keys in CI/CD

What's next