Configuring Master
This page shows how to run Axelix Master in your own environment. Pick a shape that matches how you ship the rest of your services: a standalone JAR, a Docker container, a Docker Compose stack, or a Kubernetes deployment.
Before you start
A few facts to fix in your head before you copy any of the snippets below — they save debugging time later.
- HTTP port: Master listens on
8080by default (server.port). - Database: Master supports three engines, picked automatically from the JDBC URL prefix — SQLite
(
jdbc:sqlite:), PostgreSQL (jdbc:postgresql:), and MySQL (jdbc:mysql:). The default is SQLite, with a file namedaxelix.dbnext to the working directory. Liquibase runs schema migrations on every startup. - Front-end assets: the
axelix-1.0.0.jarand the published Docker image both bundle the UI — there is nothing extra to wire up for the web interface. - Authentication: Master ships with a built-in super-admin account
admin / adminand an unset JWT signing key. In any non-throwaway environment you must change both — see Configuration reference.
Configuration reference
Every Axelix Master property lives under the axelix.master.* namespace. The tables below list each one with its
default value where one is set; entries marked (unset) must be supplied yourself before the corresponding feature can
start.
The default super-admin credentials (admin / admin) grant full control over every service Master has discovered, and
the JWT signing key has no default at all. Override both — together with axelix.master.auth.cookie.secure when serving
over HTTPS — before exposing Master to anyone other than yourself.
Supplying properties
Master is a Spring Boot application, so every property on this page can be supplied through any source Spring Boot reads — not only a config file:
- a YAML or properties config file (
application.yaml/application.properties); - a JVM system property passed with
-Don thejavacommand line; - an environment variable.
Master never restricts a property to a single source. Wherever this page documents a property such as
axelix.master.auth.jwt.signing-key, you may set it in whichever of the three fits your deployment — the same name
reaches Master identically through all of them.
This works because of Spring Boot's relaxed binding:
one logical property has several equivalent spellings, and Master reads them all as the same value. The canonical form
used throughout this page is lowercase, dot-separated, kebab-case (axelix.master.auth.jwt.signing-key). The table
below shows how that one property is written for each source:
| Source | How you write axelix.master.auth.jwt.signing-key |
|---|---|
application.yaml | nested keys axelix: → master: → auth: → jwt: → signing-key: |
application.properties | axelix.master.auth.jwt.signing-key=... |
JVM system property (-D) | -Daxelix.master.auth.jwt.signing-key=... |
| Environment variable | AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=... |
The environment-variable form follows a strict rule: upper-case the name, replace every dot with an underscore, and
drop any dashes. That is why signing-key becomes SIGNINGKEY — the dash is removed, not turned into an underscore.
A property with no dashes maps more directly: axelix.master.metrics.prometheus.enabled becomes
AXELIX_MASTER_METRICS_PROMETHEUS_ENABLED.
A -Daxelix.master.auth.jwt.signing-key=... flag and an AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=... environment variable are
interchangeable — the same value reaches Master either way, so use whichever your platform makes easier. For secrets,
prefer environment variables: the Docker and Docker Compose examples below use them because the JVM echoes the full
JAVA_TOOL_OPTIONS value (the usual carrier for -D flags in a container) to stderr on startup, which would leak
anything sensitive into the logs.
Pointing Master at a config file
The three sources above cover most setups, but it is often the case that you would want to supply a configuration to the Axelix Master from the external YAML pr properties file, for example from a file mounted into a container at a custom path.
It can be achieved by providing the property named axelix.master.config.location to Axelix Master configuration.
Since Axelix Master is a Spring Boot app, it carries the full set of location shapes Spring Boot understands: a single file,
a directory, or a comma-separated list of either, each optionally prefixed with file: or optional: (the latter tolerates a missing
file instead of failing startup). A file named here adds to Master's bundled defaults rather than replacing them —
the values it defines win, and everything it leaves out keeps its default.
| Property | Default | Description |
|---|---|---|
axelix.master.config.location | empty | Location of an external configuration file (or directory) to load on top of Master's defaults. Accepts a single path, a directory, or a comma-separated list, each with an optional file: / optional: prefix. |
Because this property decides where configuration is read from, it has to be known before that reading happens. Supply it via a JVM system property or an environment variable — not from inside the very file it points to:
java -Daxelix.master.config.location=file:/etc/axelix/master.yaml -jar master.jaror, for a container, pass it as an environment variable and mount the file in:
docker run --rm -p 8080:8080 \
-e AXELIX_MASTER_CONFIG_LOCATION=file:/etc/axelix/master.yaml \
-v /host/config/master.yaml:/etc/axelix/master.yaml:ro \
ghcr.io/axelixlabs/axelix:1.0.0Loading configuration from a Spring Cloud Config Server
If you already run a Spring Cloud Config Server to
centralise configuration across your fleet, Master can pull its own settings from it too. Master acts as a Spring Cloud
Config client: on startup it fetches configuration for its application name and merges it in. Any axelix.master.*
property on this page can therefore live in the Config Server's backing store instead of a local file.
The feature is off by default. Turn it on with axelix.master.external-config.spring-cloud-config.enabled=true and
point Master at the server with the uri property.
| Property | Default | Description |
|---|---|---|
axelix.master.external-config.spring-cloud-config.enabled | false | Load configuration from a Spring Cloud Config Server on startup. |
axelix.master.external-config.spring-cloud-config.uri | empty | Base URL of the Config Server, for example http://config-server:8888. Required when enabled=true. |
axelix.master.external-config.spring-cloud-config.name | empty | Application name the server resolves configuration for — the {application} segment of its request path. When left empty, Master uses its own name, axelix. |
axelix.master.external-config.spring-cloud-config.label | master | The {label} requested from the server, typically a Git branch or tag in the backing repository. |
axelix.master.external-config.spring-cloud-config.username | empty | Basic-auth username, when the Config Server requires HTTP Basic authentication. |
axelix.master.external-config.spring-cloud-config.password | empty | Basic-auth password, paired with username. |
Like the config-file location above, these properties decide where configuration is read from, so they must be known before that reading starts. Supply them as JVM system properties or environment variables — not from a file the Config Server would itself serve. A minimal launch looks like this:
java \
-Daxelix.master.external-config.spring-cloud-config.enabled=true \
-Daxelix.master.external-config.spring-cloud-config.uri=http://config-server:8888 \
-jar master.jarWhen the feature is enabled, Master starts in fail-fast mode. If the Config Server cannot be reached at startup, Master refuses to boot rather than starting with incomplete configuration. While the feature stays off (the default), Master never contacts a server and boots normally.
Loading configuration from HashiCorp Vault
If your organisation keeps secrets in HashiCorp Vault, Master can read its own
configuration from there too. On startup Master fetches a secret from Vault's
KV secrets engine and merges every key of that secret into its
configuration, exactly as if those keys were written in a config file. Any axelix.master.* property on this page can
therefore live in Vault. The usual split is to store the sensitive subset there — say, axelix.master.database.password
and axelix.master.auth.jwt.signing-key — and keep everything else in an ordinary config file.
The feature is off by default. Turn it on with axelix.master.external-config.spring-cloud-vault.enabled=true and point
Master at your Vault server with the uri property. With the default settings Master reads the secret stored at
secret/axelix: the secret KV mount plus Master's own application name. Version 2 of the KV engine is expected — the
default for any mount created by a current Vault. Seeding a configuration value therefore looks like this:
vault kv put secret/axelix \
axelix.master.database.password=a-strong-password \
axelix.master.auth.jwt.signing-key=cSuGCTNSJUW7yobufJTZ4C7BScamq2Yz| Property | Default | Description |
|---|---|---|
axelix.master.external-config.spring-cloud-vault.enabled | false | Load configuration from HashiCorp Vault on startup. |
axelix.master.external-config.spring-cloud-vault.uri | empty | Base URL of the Vault server, for example http://vault:8200. Required when enabled=true. |
axelix.master.external-config.spring-cloud-vault.authentication | TOKEN | Authentication method. TOKEN, APPROLE, and KUBERNETES are covered below. |
axelix.master.external-config.spring-cloud-vault.fail-fast | true | Refuse to start when Vault cannot be reached, instead of booting with incomplete configuration. |
axelix.master.external-config.spring-cloud-vault.kv.enabled | true | Read secrets from the KV secrets engine. |
axelix.master.external-config.spring-cloud-vault.kv.backend | secret | Mount path of the KV secrets engine. |
axelix.master.external-config.spring-cloud-vault.kv.application-name | axelix | Name of the secret Master reads under the mount — secret/axelix with the defaults. |
axelix.master.external-config.spring-cloud-vault.kv.default-context | application | A shared secret read in addition to the application one (secret/application with the defaults), for settings common to several applications. |
axelix.master.external-config.spring-cloud-vault.kv.profile-separator | / | Separator between the application name and a Spring profile in profile-specific secret paths. |
Under the hood Master acts as a Spring Cloud Vault client. As a
result, the whole Spring Cloud Vault connection surface is available under the same
axelix.master.external-config.spring-cloud-vault.* prefix: TLS trust material under ssl.*, namespace for Vault
Enterprise, connection and read timeouts, and further authentication methods beyond the three below. The table above
lists only the properties most setups touch.
Vault is read once, at startup. A changed secret takes effect on the next Master restart, and only the static KV engine is consulted — dynamic secrets engines, such as short-lived database credentials, are not part of this feature.
Authenticating with a static token
TOKEN is the default method, so supplying a Vault token is all it takes. This is the quickest way to try the feature
on a developer machine. A long-lived static token, however, is exactly the kind of credential Vault exists to eliminate,
so in production prefer AppRole or Kubernetes below.
| Property | Default | Description |
|---|---|---|
axelix.master.external-config.spring-cloud-vault.token | empty | Static Vault token Master authenticates with. Required for TOKEN. |
java \
-Daxelix.master.external-config.spring-cloud-vault.enabled=true \
-Daxelix.master.external-config.spring-cloud-vault.uri=http://localhost:8200 \
-Daxelix.master.external-config.spring-cloud-vault.token=hvs.6j4cuewowBGit65rheNoceI7 \
-jar master.jarAuthenticating with AppRole
AppRole is the machine-to-machine method for environments
without a platform identity: virtual machines, bare metal, Docker Compose. Master presents a role-id/secret-id pair,
and Vault exchanges it for a short-lived token scoped to the role's policy.
| Property | Default | Description |
|---|---|---|
axelix.master.external-config.spring-cloud-vault.app-role.role-id | empty | The RoleID of the AppRole Master logs in with. |
axelix.master.external-config.spring-cloud-vault.app-role.secret-id | empty | The SecretID paired with role-id. |
axelix.master.external-config.spring-cloud-vault.app-role.role | empty | Role name, optional, used for pull mode. |
axelix.master.external-config.spring-cloud-vault.app-role.app-role-path | approle | Mount path of the AppRole authentication backend. |
java \
-Daxelix.master.external-config.spring-cloud-vault.enabled=true \
-Daxelix.master.external-config.spring-cloud-vault.uri=http://vault:8200 \
-Daxelix.master.external-config.spring-cloud-vault.authentication=APPROLE \
-Daxelix.master.external-config.spring-cloud-vault.app-role.role-id=5f3420cd-2c65-4a0e-87f4-b525b8e26a54 \
-Daxelix.master.external-config.spring-cloud-vault.app-role.secret-id=f8e77c67-40b6-49aa-a548-e50c62a765b3 \
-jar master.jarThe secret-id is itself a secret, so on container platforms pass it as an environment variable
(AXELIX_MASTER_EXTERNALCONFIG_SPRINGCLOUDVAULT_APPROLE_SECRETID) rather than a -D flag.
Authenticating with Kubernetes
On Kubernetes, the Kubernetes auth method is the setup we recommend: Master authenticates with the service-account token its pod already carries, so no static credential exists in the first place. On the Vault side, create a role in the Kubernetes auth backend and bind it to Master's service account. On the Master side, name that role:
| Property | Default | Description |
|---|---|---|
axelix.master.external-config.spring-cloud-vault.kubernetes.role | empty | Vault role the login is attempted against. Required. |
axelix.master.external-config.spring-cloud-vault.kubernetes.kubernetes-path | kubernetes | Mount path of the Kubernetes authentication backend. |
axelix.master.external-config.spring-cloud-vault.kubernetes.service-account-token-file | /var/run/secrets/kubernetes.io/serviceaccount/token | Path to the pod's service-account token file. |
env:
- name: AXELIX_MASTER_EXTERNALCONFIG_SPRINGCLOUDVAULT_ENABLED
value: "true"
- name: AXELIX_MASTER_EXTERNALCONFIG_SPRINGCLOUDVAULT_URI
value: "http://vault.vault.svc:8200"
- name: AXELIX_MASTER_EXTERNALCONFIG_SPRINGCLOUDVAULT_AUTHENTICATION
value: "KUBERNETES"
- name: AXELIX_MASTER_EXTERNALCONFIG_SPRINGCLOUDVAULT_KUBERNETES_ROLE
value: "axelix-master"Like the sibling sections above, all of these properties decide where configuration is read from, so they must be known before that reading starts: supply them as JVM system properties or environment variables, never as keys inside a Vault secret.
When the feature is enabled, Master starts in fail-fast mode. If Vault cannot be reached at startup, Master refuses to boot rather than starting with incomplete configuration. While the feature stays off (the default), Master never contacts Vault and boots normally.
General
| Property | Default | Description |
|---|---|---|
axelix.master.environment | empty | Name of the environment this Master instance runs in, for example production or staging. Currently only used to populate the ECS service.environment field when structured logging is enabled. |
Database
The database engine is picked from the JDBC URL prefix. Which properties you must supply depends on the engine:
- SQLite (
jdbc:sqlite:...) — the bundled defaultjdbc:sqlite:axelix.dbworks for a first run with no extra configuration:usernameandpasswordare not used because SQLite stores data in a local file. The file lives in the working directory, so a container without a mounted volume loses its data on restart, and SQLite cannot back multiple Master replicas — switch to PostgreSQL or MySQL for anything beyond a single-node trial. - PostgreSQL (
jdbc:postgresql://...) —url,username, andpasswordare all required. The user must have permission to create tables and apply schema migrations (Liquibase runs on every startup). - MySQL (
jdbc:mysql://...) — same as PostgreSQL:url,username, andpasswordare all required, and the user needs schema-migration privileges.
| Property | Default | Description |
|---|---|---|
axelix.master.database.url | jdbc:sqlite:axelix.db | JDBC URL. The prefix selects the engine: jdbc:sqlite:, jdbc:postgresql:, or jdbc:mysql:. SQLite does not survive container restarts without a mounted volume and does not support multiple Master replicas. |
axelix.master.database.username | empty | Database user. Required when url points to PostgreSQL or MySQL. Ignored for SQLite. |
axelix.master.database.password | empty | Database password. Required when url points to PostgreSQL or MySQL. Ignored for SQLite. |
Authentication — JWT
| Property | Default | Description |
|---|---|---|
axelix.master.auth.jwt.algorithm | (unset) required | Signing algorithm. One of HMAC256, HMAC384, or HMAC512. HMAC512 is the value used in the bundled local profile. |
axelix.master.auth.jwt.signing-key | (unset) required | Secret used to sign and verify session tokens. Use a long random value. Rotate it to invalidate all sessions. |
axelix.master.auth.jwt.lifespan | 12h | Token lifetime as a Spring Duration (e.g. 30m, 12h, 7d). |
Authentication — super-admin
| Property | Default | Description |
|---|---|---|
axelix.master.auth.options.super-admin.credentials.username | admin | Built-in super-admin login. The super-admin holds every authority in the system. |
axelix.master.auth.options.super-admin.credentials.password | admin | Built-in super-admin password. Pick a long, random value. |
Authentication — local users
| Property | Default | Description |
|---|---|---|
axelix.master.auth.options.local.enabled | false | Allow user accounts stored in Master's own database to sign in. |
Authentication — OAuth2 / OIDC
How the OAuth2 / OIDC sign-in flow works end-to-end is covered on the Authentication page.
When axelix.master.auth.options.oauth2.enabled=true, the following properties become required:
axelix.master.auth.options.oauth2.issuer-uriaxelix.master.auth.options.oauth2.client-idaxelix.master.auth.options.oauth2.client-secretaxelix.master.auth.options.oauth2.base-url
| Property | Default | Description |
|---|---|---|
axelix.master.auth.options.oauth2.enabled | false | Enable OIDC sign-in. |
axelix.master.auth.options.oauth2.issuer-uri | (unset) required | OIDC issuer base URL. Master reads /.well-known/openid-configuration from here to discover endpoints. Required whenever oauth2.enabled=true. |
axelix.master.auth.options.oauth2.client-id | (unset) required | Client identifier registered with the OIDC provider. Required whenever oauth2.enabled=true. |
axelix.master.auth.options.oauth2.client-secret | (unset) required | Client secret registered with the OIDC provider. Required whenever oauth2.enabled=true. |
axelix.master.auth.options.oauth2.base-url | (unset) required | Public base URL of this Master instance, used to build the OAuth2 callback redirect URI. Required whenever oauth2.enabled=true. |
axelix.master.auth.options.oauth2.scopes | openid | Space-separated scopes requested during the authorization code flow. openid is appended automatically if missing. |
axelix.master.auth.options.oauth2.role-attribute-path | (unset) | JMESPath expression evaluated against the userinfo response to resolve an Axelix role. If unset, every OIDC user becomes a VIEWER. |
Authentication — session cookie
| Property | Default | Description |
|---|---|---|
axelix.master.auth.cookie.secure | false | Set to true whenever Master is served over HTTPS so the session cookie is not sent over plain HTTP. |
Discovery — auto-discovery
Auto-discovery lets Master find Spring Boot Starter–equipped applications without each one having to call in. On the
schedule set by broadcast.schedule, Master queries the Kubernetes API for Services in the configured namespaces,
filters them by label, and probes /actuator/axelix-metadata on each match using a JWT it issues itself with
axelix.master.auth.jwt.signing-key. A successful probe registers the instance; a probe failure drops it from the list.
To turn the feature on, set axelix.master.discovery.auto.enabled=true (default false, i.e. off). Kubernetes is
currently the only supported discovery platform.
When Master runs inside the cluster as a pod, the Kubernetes connection settings (kube-apiserver-url,
sa-token-path, ca-cert-path) default to in-pod values and need no override, The starter side just has to share the JWT signing key —
the starter exposes axelix-metadata by itself. When Master runs outside the cluster, point those three to the
external API URL, a token file with the required RBAC, and the cluster CA path. Use filters.namespaces and
filters.labels to scope the scan.
| Property | Default | Description |
|---|---|---|
axelix.master.discovery.auto.enabled | false | Enable platform-driven autodiscovery of managed services. |
axelix.master.discovery.auto.broadcast.schedule | 0 * * * * * | Cron expression that triggers the discovery scan. |
axelix.master.discovery.auto.kubernetes.kube-apiserver-url | https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS} | URL of the Kubernetes API server. |
axelix.master.discovery.auto.kubernetes.sa-token-path | /var/run/secrets/kubernetes.io/serviceaccount/token | Path inside the Master pod where the ServiceAccount token is mounted. |
axelix.master.discovery.auto.kubernetes.ca-cert-path | /var/run/secrets/kubernetes.io/serviceaccount/ca.crt | Path inside the Master pod where the kube-apiserver CA certificate is mounted. |
axelix.master.discovery.auto.kubernetes.filters.namespaces | default | Comma-separated list of namespaces to scan. |
axelix.master.discovery.auto.kubernetes.filters.labels | empty | Map of label key/value pairs a Kubernetes Service must match to be picked up by discovery. |
Discovery — self-registration
Self-registration is the opposite direction of autodiscovery: Master accepts heartbeats pushed from a Spring Boot
Starter–equipped application instead of scanning for it. Each heartbeat carries the instance's actuator URL, instance
name, and a JWT signed with the shared axelix.master.auth.jwt.signing-key. Master stores the registration and treats
it as a known instance until the heartbeats stop. If no heartbeat arrives within eviction.heartbeat-timeout, the
eviction sweep (scheduled by eviction.schedule) drops the instance.
The endpoint is POST /api/internal/service/register. It is gated by the same JWT signing key set under
axelix.master.auth.jwt.* — without a matching key, heartbeats are rejected.
On the Master side there are no required overrides — every property here has a working default. The feature is on
by default (enabled=true), set it to false only if you want Master to refuse all heartbeats. The starter-side
properties that go into the heartbeat (master-url, instance-actuator-url, instance-name) are documented in the
Configuring Spring Boot Starter page.
| Property | Default | Description |
|---|---|---|
axelix.master.discovery.self-registration.enabled | true | Accept self-registration heartbeats from Spring Boot Starter–equipped applications. Set to false to refuse them. |
axelix.master.discovery.self-registration.eviction.heartbeat-timeout | 45s | Drop a self-registered instance after this long without a heartbeat. |
axelix.master.discovery.self-registration.eviction.schedule | 0 * * * * * | Cron expression that triggers the eviction sweep. |
MCP server
| Property | Default | Description |
|---|---|---|
axelix.master.mcp-server.enabled | true | Expose the bundled Model Context Protocol server at /api/mcp. |
Structured logging
By default, Master logs plain text to the console (i.e. the process stdout). Set axelix.master.logging.json.enabled=true
to switch to Elastic Common Schema (ECS) structured JSON
logging instead — the only structured format Master supports today. ECS is a JSON-based logging format; reach for it
when logs are shipped to an aggregator such as Elasticsearch or a similar ECS-aware pipeline.
| Property | Default | Description |
|---|---|---|
axelix.master.logging.json.enabled | false | Switch console logging from plain text to ECS-formatted structured JSON. |
Metrics
Prometheus
Master exposes a Prometheus scrape endpoint once you turn it on with axelix.master.metrics.prometheus.enabled=true.
It responds at /api/actuator/prometheus and does not require authentication.
By default it uses the same port as the rest of Master (server.port). Set axelix.master.metrics.prometheus.port to
use a different port instead, useful when you want to restrict access to scraping separately from the main UI and
API.
Attach common tags to every exported metric with axelix.master.metrics.prometheus.tags.<name>=<value>, for example
a deployment region or environment.
Because the endpoint is unauthenticated, anyone who can reach Master over the network can read metric values and any
configured common tags. Restrict /api/actuator/prometheus to trusted Prometheus infrastructure with a network
policy, ingress rule, or an equivalent perimeter control.
| Property | Default | Description |
|---|---|---|
axelix.master.metrics.prometheus.enabled | false | Expose the Prometheus scrape endpoint at /api/actuator/prometheus. |
axelix.master.metrics.prometheus.port | (unset) | Port for the Prometheus endpoint. Defaults to server.port. |
axelix.master.metrics.prometheus.tags | empty | Common tags applied to every exported metric. |
axelix:
master:
metrics:
prometheus:
enabled: true
tags:
region: eu-west-1Using a separate port instead:
axelix:
master:
metrics:
prometheus:
enabled: true
port: 9404OTLP
Master can push its own JVM, HTTP, database-pool, and other Micrometer metrics to an OTLP-compatible collector over HTTP/protobuf. This exports metrics produced by Master itself, not metrics read from services connected through an Axelix starter. Export of metrics via OTLP is disabled by default (see properties below).
| Property | Default | Description |
|---|---|---|
axelix.master.metrics.otlp.enabled | false | Enables periodic OTLP metrics export. |
axelix.master.metrics.otlp.url | http://localhost:4318/v1/metrics | Complete HTTP/protobuf metrics endpoint. Include the /v1/metrics path. |
axelix.master.metrics.otlp.step | 1m | Interval between exports, expressed as a Spring Duration. |
axelix.master.metrics.otlp.headers.* | empty | Custom request headers, for example an authorization header. Inject secret values at runtime. |
axelix.master.metrics.otlp.compression-mode | none | Request compression mode: none or gzip. |
axelix:
master:
metrics:
otlp:
enabled: true
url: http://otel-collector:4318/v1/metrics
step: 30s
headers:
Authorization: ${OTLP_AUTHORIZATION_HEADER}These settings can equally be supplied as environment variables or JVM system properties — see Supplying
properties for the relaxed-binding rules that map, for example,
axelix.master.metrics.otlp.enabled to AXELIX_MASTER_METRICS_OTLP_ENABLED.
Prefer injecting authentication headers from your deployment platform's secret store instead of committing them to a configuration file.
Run as a JAR
At its core Axelix Master is just a JVM process, so it is shipped as the JAR, so it can be launched with the simple
java -jar master.jar command (of course, its going to be a bit more complicated than that). If for any reason you do
not use containerization, then this options is for you.
1. Download the JAR
Grab the Axelix Master JAR attached to the release you want from the Releases page. The published artifact already bundles the UI, so there is nothing else to build.
2. Configure and start Master
At minimum, provide a JWT signing key and an algorithm; everything else can stay on defaults for a first run. You can
hand Master that configuration in any of three interchangeable ways — Axelix Master reads them all identically (see
Supplying properties): as environment variables or -D JVM system properties on the
command line (both shown next), or from an external config file such as application.yaml / application.properties.
Environment variables
Handy when your shell or process manager already exports the values:
export AXELIX_MASTER_AUTH_JWT_ALGORITHM=HMAC512
export AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=replace-with-a-long-random-secret
java -jar master.jarJVM system properties (-D)
Convenient for a quick one-off launch:
java \
-Daxelix.master.auth.jwt.algorithm=HMAC512 \
-Daxelix.master.auth.jwt.signing-key=replace-with-a-long-random-secret \
-jar master.jarExternal config file
Keep the same settings in a YAML or properties file and point Master at it with axelix.master.config.location; see
Pointing Master at a config file for the accepted location shapes.
axelix:
master:
auth:
jwt:
algorithm: HMAC512
signing-key: replace-with-a-long-random-secretjava -Daxelix.master.config.location=file:/etc/axelix/master.yaml -jar master.jarMaster is then reachable at http://localhost:8080. Sign in with the super-admin credentials you configured.
Run with Docker
If you have containerization support, then the recommended way to install Axelix Master is via the docker image. The release
pipeline publishes the image to GitHub Container Registry, so the name of the image follows the pattern
ghcr.io/axelixlabs/axelix:1.0.0
The snippets below pin 1.0.0 for illustration. Check the Releases
page for the latest published tag and substitute it in your own
commands.
Run command
A minimal run, suitable for a first look:
docker run --rm -p 8080:8080 \
-e AXELIX_MASTER_AUTH_JWT_ALGORITHM=HMAC512 \
-e AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=replace-with-a-long-random-secret \
ghcr.io/axelixlabs/axelix:1.0.0A few notes about this command:
- The image runs as the non-root user
axelixand uses an Ahead-of-Time class loading cache built at image-build time, so startup is faster than a plain JAR launch. - The default database is still SQLite at the working directory (
/application/axelix.db). The file lives inside the container's writable layer and disappears when the container is removed. Either switch to PostgreSQL/MySQL (see the next section) or mount a volume at the SQLite path.
Ways to supply configuration
Prefer environment variables — inside a container they are the safest place for configuration, and the only option that keeps secrets out of the startup log.
Individual environment variables
Supplying configuration via individual environment variables follows the relaxed-binding rules from Supplying
properties, passed with -e. This is what the run command above uses:
docker run --rm -p 8080:8080 \
-e AXELIX_MASTER_DATABASE_URL=jdbc:postgresql://db.internal:5432/axelix \
-e AXELIX_MASTER_DATABASE_USERNAME=axelix \
-e AXELIX_MASTER_DATABASE_PASSWORD=... \
-e AXELIX_MASTER_AUTH_JWT_ALGORITHM=HMAC512 \
-e AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=replace-with-a-long-random-secret \
ghcr.io/axelixlabs/axelix:1.0.0System Properties (-D) via JAVA_TOOL_OPTIONS
If you supply -D system properties through JAVA_TOOL_OPTIONS the JVM will apply this variable to the java command
automatically, so it is a convenient hook for JVM tuning such as heap sizing.
The JVM prints the full value of JAVA_TOOL_OPTIONS to stderr on startup (Picked up JAVA_TOOL_OPTIONS: ...), so
whatever you put there lands in docker logs and any log collector. Never place the JWT signing key, the super-admin
password, or database credentials in it — pass those as discrete -e environment variables instead.
The example of using it can be found below:
docker run --rm -p 8080:8080 \
-e JAVA_TOOL_OPTIONS="-Xmx512m -Daxelix.master.metrics.prometheus.enabled=true" \
-e AXELIX_MASTER_AUTH_JWT_ALGORITHM=HMAC512 \
-e AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=replace-with-a-long-random-secret \
ghcr.io/axelixlabs/axelix:1.0.0External config file
You can also supply configuration via an external config file. Mount a YAML or properties file into the container and point Master at it with
axelix.master.config.location (see Pointing Master at a config file). Keep
secrets out of a file baked into or committed alongside the image; pass those as discrete -e environment variables
just as above.
axelix:
master:
metrics:
prometheus:
enabled: trueAnd then passing it as the volume to the docker command:
docker run --rm -p 8080:8080 \
-v /host/config/master.yaml:/etc/axelix/master.yaml:ro \
-e AXELIX_MASTER_CONFIG_LOCATION=file:/etc/axelix/master.yaml \
-e AXELIX_MASTER_AUTH_JWT_ALGORITHM=HMAC512 \
-e AXELIX_MASTER_AUTH_JWT_SIGNINGKEY=replace-with-a-long-random-secret \
ghcr.io/axelixlabs/axelix:1.0.0Run with Docker Compose
Logically, it is fully possible to run Axelix Master in the Docker Compose deployment.
The example below is a complete stack you can drop into a new docker-compose.yaml, edit, and start with docker compose up -d. The master service is pinned to 1.0.0 here — swap in the latest tag from the Releases
page before shipping.
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: axelix
POSTGRES_USER: ${AXELIX_DB_USERNAME:?set AXELIX_DB_USERNAME}
POSTGRES_PASSWORD: ${AXELIX_DB_PASSWORD:?set AXELIX_DB_PASSWORD}
volumes:
- axelix-pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 5s
retries: 10
master:
image: ghcr.io/axelixlabs/axelix:1.0.0
depends_on:
postgres:
condition: service_healthy
ports:
- "8080:8080"
environment:
AXELIX_MASTER_DATABASE_URL: jdbc:postgresql://postgres:5432/axelix
AXELIX_MASTER_DATABASE_USERNAME: ${AXELIX_DB_USERNAME}
AXELIX_MASTER_DATABASE_PASSWORD: ${AXELIX_DB_PASSWORD}
AXELIX_MASTER_AUTH_JWT_ALGORITHM: HMAC512
AXELIX_MASTER_AUTH_JWT_SIGNINGKEY: ${AXELIX_JWT_SIGNING_KEY}
AXELIX_MASTER_AUTH_OPTIONS_SUPERADMIN_CREDENTIALS_USERNAME: ${AXELIX_ADMIN_USERNAME}
AXELIX_MASTER_AUTH_OPTIONS_SUPERADMIN_CREDENTIALS_PASSWORD: ${AXELIX_ADMIN_PASSWORD}
AXELIX_MASTER_AUTH_COOKIE_SECURE: "false"
volumes:
axelix-pgdata:Each AXELIX_MASTER_* key maps to the matching property through relaxed binding (see Supplying
properties). Passing them as discrete environment variables — rather than bundling them into
JAVA_TOOL_OPTIONS — keeps the signing key and passwords out of the container's startup log.
Another option is to provide the values via an .env file next to the Compose file:
AXELIX_DB_USERNAME=axelix
AXELIX_DB_PASSWORD=...
AXELIX_JWT_SIGNING_KEY=...
AXELIX_ADMIN_USERNAME=admin
AXELIX_ADMIN_PASSWORD=...AXELIX_MASTER_AUTH_COOKIE_SECURE is false in this example because the snippet exposes Master over plain HTTP for
local testing. Put Master behind a TLS-terminating proxy and flip it to true for any real deployment.
Supplying configuration via a mounted file
Instead of listing every non-secret setting under environment, you can mount a YAML or properties file into the
master service and point Master at it with axelix.master.config.location (see Pointing Master at a config
file):
axelix:
master:
database:
url: jdbc:postgresql://postgres:5432/axelix
auth:
cookie:
secure: false master:
image: ghcr.io/axelixlabs/axelix:1.0.0
volumes:
- ./master.yaml:/etc/axelix/master.yaml:ro
environment:
AXELIX_MASTER_CONFIG_LOCATION: file:/etc/axelix/master.yaml
AXELIX_MASTER_AUTH_JWT_ALGORITHM: HMAC512
AXELIX_MASTER_AUTH_JWT_SIGNINGKEY: ${AXELIX_JWT_SIGNING_KEY}Keep secrets — the JWT signing key, the super-admin password, database credentials — out of a committed file, and pass those as discrete environment variables as shown above.
Run on Kubernetes
Use this when you want Master to live in the same cluster as the services it monitors and to discover them automatically.
Axelix ships a first-party Helm chart, so you do not need to assemble manifests by hand:
- Chart source: github.com/axelixlabs/helm-charts —
README.mddocuments every value the chart exposes, including database connection, JWT, super-admin credentials, autodiscovery, RBAC, and Ingress. - Published package: artifacthub.io/packages/helm/axelix/axelix — pulls the chart from the Axelix Helm repository and lists the available versions.
A typical install boils down to:
helm repo add axelix https://axelixlabs.github.io/helm-charts
helm repo update
helm install axelix axelix/axelix \
--namespace axelix --create-namespace \
--values values.yamlPut your overrides — at minimum axelix.master.auth.jwt.signing-key, the super-admin password, and the database
connection — into values.yaml. The chart wires the ServiceAccount + RBAC required for in-cluster autodiscovery for
you.
Expose the Service through your cluster's usual mechanism — an Ingress or a Gateway — for a quick check.
Bringing your own chart
Some teams keep their own Helm chart or raw manifests rather than the first-party chart. That is fine. There are just a couple of things that need to be emphasized.
Automatic Discovery
During auto-discovery, Axelix Master queries the K8S control plane REST API. In order to do that, the K8S ServiceAccount that is bundled into the pod must have the appropriate permissions. The official chart wires them up for you.
This matters only when axelix.master.discovery.auto.enabled=true — without auto-discovery Master needs none of the
permissions below.
Here is what to keep in mind when writing custom Helm Chart:
1. Master must reach the Kubernetes API. As mentioned, in auto-discovery mode Master calls the cluster's control-plane API on the
schedule set by broadcast.schedule, listing Services and the Pods behind them in each configured namespace. By
default, Axelix Master reads the connection details from the standard in-pod locations:
- The URL of the API server from
KUBERNETES_SERVICE_HOST/KUBERNETES_SERVICE_PORT_HTTPS, which Kubernetes injects into every pod; - The ServiceAccount token is taken from
/var/run/secrets/kubernetes.io/serviceaccount/token; - The CA certificate from
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt.
Those are the defaults of the kube-apiserver-url, sa-token-path, and ca-cert-path properties listed under
auto-discovery. Keep the defaults, and your only job is to make the token and CA land at
those paths. The next two points cover that.
2. The pod needs a ServiceAccount with its token mounted. Create a ServiceAccount, set serviceAccountName on the
Deployment, and leave automountServiceAccountToken: true so the token and CA appear at the paths above. Without a
mounted token Master has no credentials to present, and every scan fails with an authorization error.
3. That ServiceAccount needs RBAC to read workloads. A bare ServiceAccount can authenticate but is allowed nothing.
Grant it get, list, and watch on pods, services, and endpoints in each namespace you want discovered, then
bind the created Role to the ServiceAccount. The namespaces you grant must match filters.namespaces: Master scans exactly
those, and a namespace it cannot read returns no instances.
Put together, the minimum for a single discovered namespace looks like this:
apiVersion: v1
kind: ServiceAccount
metadata:
name: axelix-master
namespace: axelix
automountServiceAccountToken: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: axelix-master-role
namespace: default # a namespace listed in filters.namespaces
rules:
- apiGroups: [""]
resources: ["pods", "services", "endpoints"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: axelix-master-role-binding
namespace: default
subjects:
- kind: ServiceAccount
name: axelix-master
namespace: axelix # the namespace the ServiceAccount lives in
roleRef:
kind: Role
name: axelix-master-role
apiGroup: rbac.authorization.k8s.ioThen point the Deployment at that ServiceAccount:
spec:
template:
spec:
serviceAccountName: axelix-master
containers:
- name: axelix-master
image: ghcr.io/axelixlabs/axelix:1.0.0In K8S, a Role is a namespaced object, so repeat the Role and RoleBinding once per namespace in filters.namespaces, all referencing
the same ServiceAccount. When the ServiceAccount lives in a different namespace from the workloads, the RoleBinding
subject must still name the ServiceAccount's own namespace, as shown above.
The official's chart templates/rbac.yaml
and templates/serviceaccount.yaml
are the reference for exactly this. You may run helm template against the chart and copy the rendered ServiceAccount, Role,
and RoleBinding into your own manifests if you want a starting point.
Related
- Configuring Spring Boot Starter — wire your monitored applications so Master can see them.
- What is Master? — background on what Master does once it is running.
- Authentication — deeper look at JWT, super-admin, and OAuth2/OIDC.