Run configuration

By this we mean the properties you can set to control the behaviour of different servers. As of 1.5.0 all FeatureHub controller properties are available as environment variables using the same case. If you have been using the mechanism introduced in 1.4.1 this still works but isn’t recommended going forward.

If you are using a system like Kubernetes, you can mount these properties in /etc/app-config/ and /etc/app-config/

Database configuration

All subsystems that talk to the database take these parameters. Even if you are using environment variables, we recommend using lower case so the database connections are correctly configured.

  • db.url - the jdbc url of the database server.

  • db.username - the username used to log in.

  • db.password - the password for the user

  • db.minConnections - the minimum number of connections to hold open (default 3)

  • db.maxConnections - the maximum connections to open to the db (default 100)

  • db.pstmtCacheSize - the prepared statement cache size (no default)

The library we use - ebean - supports a number of other configuration parameters

Database Read Replicas

We also support Read Replicas which are useful for deployments of edge-rest. We do not recommend them for mr, party-server or party-server-ish deployments as read replicas have to behave like being up to a couple of seconds out is ok. This is fine for edge-rest as it’s major functionality is reading via a GET. To use a read replica db. prefixes use db-replica prefixes to configure a read replica, where it is and how it should be connected to. Typically an edge-rest deployment will configure both of these (db and db-replica parameters) but the corresponding mr will not.


If you are using the Streaming version of FeatureHub, then you may need to configure your NATS urls. If you have only once instance of a party-server, you can leave it as the default. If you have deployed Option 2, or you have multiple servers with Option 1a, you will need to make sure your NATS servers are configured correctly.

  • nats.urls - a comma separated list of NATs servers.

NATS works by having the clients tell the servers where each other are, so the NATS servers need to be routable (i.e. they must be able to talk to each other) but do not need to be explicitly told about each other.

Management Repository

The following properties can be set:

  • passwordsalt.iterations (1000) - how many iterations it will use to salt passwords

  • cache.pool-size (10) - how many threads it will allocate to publishing changes to Dacha and SSE

  • feature-update.listener.enable (true) - whether this MR should listen to the same topic as the Dacha’s and respond if they are empty

  • (production) - the name given to the automatically created production environment. It will be tagged "production".

  • environment.production.desc (production) - the description field for same.

  • register.url [deprecated] - the url used for registration. The front-end should strip the prefix off this and add its own relative one. The format has to be register.url=http://localhost:8085/register-url?token=%s - if your site is for example, it would be register.url= This is honoured but no longer required and it is recommended to be removed.

  • ("Administrators") - the suffix added to a portfolio group when a portfolio is created for the first time, it needs an Admin group. So a portfolio called "Marketing" would get an admin group called "Marketing Administrators" created.

  • web.asset.location=/var/www/html/intranet - can be set optionally if you are intending to serve the Admin web app on the intranet without public internet access. We supply this application build already preloaded with all necessary assets. Available in FeatureHub v1.5.4 and higher.

  • cache-control.web.index - this allows you to set the Cache-Control header on the index.html file. It is set by default to no-store, max-age=0 preventing any caching, so as new versions roll out, they are correctly picked up.

  • cache-control.web.other - this sets the cache control on all of the other content of the website, which is essentially considered to be versioned. This data should never change and it is set by default to max-age=864000 - or about 10 days.

Dacha Config

The following properties can be set (that are meaningful):

  • nats.urls - a comma separated list of NATs servers

  • cache.timeout - how long the server will attempt to find and resolve a master cache before moving onto the next step (in ms, default = 5000)

  • cache.complete-timeout - how long it will wait after another cache has negotiated master before it expects to see data (in ms, default = 15000)

  • cache.pool-size - the number of threads in pool for doing "work" - defaults to 10

Edge (all) Config

  • jersey.cors.headers - a list of CORS headers that will be allowed, specifically for browser support

  • update.pool-size (10) - how many threads to allocate to processing incoming updates from NATs. These are responses to feature requests and feature updates coming from the server.

  • edge.cache-control.header - specifically for the GET (polling) API, this lets your infrastructure limit how often the clients can actually poll back. It would allow an infrastructure team to override individual development teams on how often they wish polling to take place. It is generally not recommended to do this, but there may be situations where it makes sense.

Edge (Streaming) Config

  • nats.urls - a comma separated list of NATs servers

  • listen.pool-size (10) - how many threads to allocate to processing incoming requests to listen. This just takes the request, decodes it and sends it down via NATs and releases.

  • edge.sse.drop-after-seconds (30) - how many seconds a client is allowed to listen for before being kicked off. Used to ensure connections don’t go stale. This was previously named maxSlots and a valid in that field is recognized.

  • edge.dacha.delay-slots (10) - if Dacha is unavailable because it does not have a full cache, it will reject the request. For SSE, this creates a sliding window of a random delay in seconds, meaning a connection will be dropped in 1-10 seconds (by default). This is designed to prevent reconnect storms as infrastructure is restarted.

  • edge.sse.heartbeat-period (0) - if defined, Edge will attempt to send heartbeat signals down the SSE connection for the duration of the connection while it is alive. If you set edge.sse.drop-after-seconds to 0, then the SSE connection will stay open, sending heartbeat signals until the remote system drops the connection. This allows the heartbeat to be used as well as or instead of kicking SSE connections off to ensure ghost connections.

  • dacha.url.<cache-name> = url - this is only relevant if you are running split servers - so Dacha and Edge run in their own containers. You need to tell Edge where Dacha is located. The default cache is called default, so it will expect one called `dacha.url.default and the url. In the sample docker-compose where they are split, the hostname for Dacha is dacha, so this is dacha.url.default=http://localhost:8094. This isn’t required for the Party Server because communication is internal.

Edge (REST only) Config

Edge REST uses the database, so it also needs the database config. Edge-REST is bundled as a separate container, so it can be run and exposed directly instead of being exposed along with the Admin site.

Party Server

The party server honours all values set by the Management Repository, Dacha and the SSE-Edge.


The party-server-ish honours all the values set by the Management Repository and Edge REST.

Common to all servers

All servers expose metrics and health checks. The metrics are for Prometheus and are on /metrics, liveness is on /health/liveness and readyness on /health/readyness. Furthermore, every listening port responds with a 200 on a request to / so that load balancers that aren’t configured to listen to the proper readiness checks will function.

Each different server has a collection of what things are important to indicate aliveness. The server.port setting will expose these endpoints, which means they are available to all of your normal API endpoints as well. In a cloud-native environment, which FeatureHub is aimed at, this is rarely what you want. So FeatureHub has the ability to list these endpoints on a different port.

  • monitor.port (undefined) - if not defined, it will expose the metrics and health on the server port. If not, it will expose them on this port (and not on the server port).

  • featurehub.url-path - allows to configure base path (context root) other than "/". This will set the base path in the index.html of the FeatureHub web app and the backend. Note, this is an offset, not a full domain name, e.g. featurehub.url-path=/foo/featurehub. In case if the front-end is decoupled on a CDN, the base bath needs to be configured directly in index.html by setting: <base href="/foo/featurehub/"> (note the trailing slash).

  • cache-control.api - allows the configuration of the Cache-Control headings on all GET based API calls. This allows you to put a CDN in front of FeatureHub and ensure the CDN does not cache any headers. It is on by default. See also the cache-control.web configuration for MR and Party Server.

  • cache-control.api.enabled - set this to false if you wish to disable the Cache Control headers for APIs.

  • connect.logging.environment - this is a comma separated value list that lets you pick up values from environment variables that get added directly to your logs. It is typically used in Kubernetes deploys to allow you to extract information from the k8s deploy and put it in environment variables and have them logged. The format is <ENV-VAR>=<log-key>. You can use . notation to split it into objects.

Generated Logs
{"@timestamp":"2022-01-22T18:12:56.767+1300","message":"1 * Server has received a request on thread grizzly-http-server-0\n1 > GET http://localhost:8903/info/version\n1 > accept: */*\n1 > host: localhost:8903\n1 > user-agent: curl/7.77.0\n","priority":"TRACE","path":"jersey-logging","thread":"grizzly-http-server-0","kubernetes":{"node":"peabody","zone":"amelia"},"host":"thepolishedbrasstack.lan","":"received: GET - http://localhost:8903/info/version"}
  • audit.logging.web.header-fields - a comma separated list of fields that will be extracted out of each web request and put into a field in the JSON logs output by the server. All headers are grouped into an object called http-headers. Headers by definition are case insensitive. Available from 1.5.5. An example:

Generated Logs
{"@timestamp":"2022-01-22T14:46:19.374+1300","message":"txn[1106] Begin","priority":"TRACE","path":"io.ebean.TXN","thread":"grizzly-http-server-0","host":"my-computer","http-headers":{"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36","origin":"http://localhost:53000","Sec-fetch-Mode":"cors"}}
  • audit.logging.user - if this is set to true (it is false by default) then the user’s ID and email will be logged against each of their requests where it is known. It appears in a user object with id and email as components. Available from 1.5.5. An example

Generated Logs
{"@timestamp":"2022-01-22T14:58:15.854+1300","message":"txn[1109] select, t0.when_archived, t0.feature_key, t0.alias,, t0.secret,, t0.value_type, t0.when_updated, t0.when_created, t0.version, t0.fk_app_id from fh_app_feature t0 where = ?; --bind(2b86605b-1a81-4fc7-80b7-17edc5e3206e, ) --micros(697)","priority":"DEBUG","path":"io.ebean.SQL","thread":"grizzly-http-server-1","host":"my-computer","user":{"id":"68c09a3d-6e44-4379-bfc1-3e75af59af38","email":""}}

Common to Party, SSE Edge and Management Repository

  • server.port (8903) - the server port that the server runs on. it always listens to (all network interfaces)

  • server.gracePeriodInSeconds (10) - this is how long the server will wait for connections to finish after it has stopped listening to incoming traffic

Jersey specific config around logging is from here: Connect jersey Common

  • jersey.exclude

  • jersey.tracing

  • jersey.bufferSize (8k) - how much data of a body to log before chopping off

  • jersey.logging.exclude-body-uris - urls in which the body should be excluded from the logs

  • jersey.logging.exclude-entirely-uris - urls in which the entire context should be excluded from the logs. Typically you will include the /health/liveness and /health/readyness API calls along with the /metrics from this. You may also wish to include login urls.

  • jersey.logging.verbosity - the default level of verbosity for logging HEADERS_ONLY, - PAYLOAD_TEXT, - PAYLOAD_ANY

Runtime Monitoring


The Prometheus endpoint is on /metrics for each of the servers. Extensive metrics are exposed on all services by default. It is recommended that for public facing sites, you separate the monitoring port from the server port, so you don’t expose your health check or metrics endpoints to the public.

Health and Liveness checks

A server is deemed "Alive" once it is in STARTING or STARTED mode. It is deemed "Ready" when it is in STARTED mode. All servers put themselves into STARTING mode as soon as they are able, and then STARTED once the server is actually listening. The urls are:

  • /health/liveness

  • /health/readyness