Server configuration
Flagr has no config file. Every knob is an environment variable bound at startup to one struct: pkg/config/env.go. That struct is the source of truth. When this page and the code disagree, the code wins.
This page embeds pkg/config/env.go from the repo tree at docs build time (every env tag and default), then a short operator guide for the variables you actually touch. Niche knobs may appear only in the source block.
Deploy recipes: Self-hosting.
Source (pkg/config/env.go)
The block below is the checked-in source at the commit used to build the docs site. You can also open it on GitHub.
package config
import "time"
// Config is the whole configuration of the app
var Config = struct {
// Host - Flagr server host
Host string `env:"HOST" envDefault:"localhost"`
// Port - Flagr server port
Port int `env:"PORT" envDefault:"18000"`
// LogrusLevel sets the logrus logging level
LogrusLevel string `env:"FLAGR_LOGRUS_LEVEL" envDefault:"info"`
// LogrusFormat sets the logrus logging formatter
// Possible values: text, json
LogrusFormat string `env:"FLAGR_LOGRUS_FORMAT" envDefault:"text"`
// PProfEnabled - to enable the standard pprof of golang's http server
PProfEnabled bool `env:"FLAGR_PPROF_ENABLED" envDefault:"true"`
// MiddlewareVerboseLoggerEnabled - to enable the negroni-logrus logger for all the endpoints useful for debugging
MiddlewareVerboseLoggerEnabled bool `env:"FLAGR_MIDDLEWARE_VERBOSE_LOGGER_ENABLED" envDefault:"true"`
// MiddlewareVerboseLoggerExcludeURLs - to exclude urls from the verbose logger via comma separated list
MiddlewareVerboseLoggerExcludeURLs []string `env:"FLAGR_MIDDLEWARE_VERBOSE_LOGGER_EXCLUDE_URLS" envDefault:"" envSeparator:","`
// MiddlewareGzipEnabled - to enable gzip middleware
MiddlewareGzipEnabled bool `env:"FLAGR_MIDDLEWARE_GZIP_ENABLED" envDefault:"true"`
// RateLimiterPerFlagPerSecondConsoleLogging - to rate limit the logging rate
// per flag per second
RateLimiterPerFlagPerSecondConsoleLogging int `env:"FLAGR_RATELIMITER_PERFLAG_PERSECOND_CONSOLE_LOGGING" envDefault:"100"`
// EvalEnableDebug - controls if we want to return evaluation debugging information back to the api requests
// Note that this is a global switch:
// if it's disabled, no evaluation debug info will be returned.
// if it's enabled, it respects evaluation request's enableDebug field
EvalDebugEnabled bool `env:"FLAGR_EVAL_DEBUG_ENABLED" envDefault:"true"`
// EvalLoggingEnabled - to enable the logging for eval results
EvalLoggingEnabled bool `env:"FLAGR_EVAL_LOGGING_ENABLED" envDefault:"true"`
// EvalCacheRefreshTimeout - timeout of getting the flags data from DB into the in-memory evaluation cache
EvalCacheRefreshTimeout time.Duration `env:"FLAGR_EVALCACHE_REFRESHTIMEOUT" envDefault:"59s"`
// EvalCacheRefreshInterval - time interval of getting the flags data from DB into the in-memory evaluation cache
EvalCacheRefreshInterval time.Duration `env:"FLAGR_EVALCACHE_REFRESHINTERVAL" envDefault:"3s"`
// EvalOnlyMode - health, evaluation, and eval-cache export only; the UI is read-only.
// json_file / json_http set this automatically via setupEvalOnlyMode.
EvalOnlyMode bool `env:"FLAGR_EVAL_ONLY_MODE" envDefault:"false"`
// UIEnabled controls whether the Flagr UI is served.
// Set to false for backend-only deployments where the UI is not needed.
UIEnabled bool `env:"FLAGR_UI_ENABLED" envDefault:"true"`
// EvalBatchSize - maximum number of total evaluations allowed in a single batch request.
// This is calculated as: len(entities) * (len(flagIDs) + len(flagKeys) + estimated_flags_from_tags).
// Set to 0 to disable the limit (default). Enable this for additional DoS protection.
// Note: Duplicate flagKeys and flagIDs are always deduplicated regardless of this setting.
//
// Example calculation for 10k flags:
// - With 2 entities and 10 flagIDs + 10 flagKeys: 2 * (10 + 10) = 40 evaluations
// - With 2 entities and 2 tags (~100 flags each): 2 * 100 = 200 evaluations
// A reasonable limit might be 500-1000 for typical use cases.
EvalBatchSize int `env:"FLAGR_EVAL_BATCH_SIZE" envDefault:"0"`
// EvalGetMaxURLBytes - maximum length of the raw query string on GET /evaluation and GET /evaluation/batch.
// Set to 0 to disable (default 8192). Exceeding the limit returns 400; use POST when payloads are large.
EvalGetMaxURLBytes int `env:"FLAGR_EVAL_GET_MAX_URL_BYTES" envDefault:"8192"`
// InjectedContextEnabled - enables built-in context injection into entityContext.
// When true, @ts, @ts_hour, @ts_weekday, @ts_month are always injected.
// HTTP headers listed in InjectedContextHTTPHeaders are injected as @http_* keys.
InjectedContextEnabled bool `env:"FLAGR_INJECTED_CONTEXT_ENABLED" envDefault:"false"`
// InjectedContextHTTPHeaders - comma-separated list of HTTP header names to expose as @http_* context keys.
// Example: "X-Environment,X-Tenant-ID,Host"
InjectedContextHTTPHeaders []string `env:"FLAGR_INJECTED_CONTEXT_HTTP_HEADERS" envDefault:"" envSeparator:","`
// InjectedContextHTTPHeaderPrefixes - comma-separated list of HTTP header prefixes to auto-inject as @http_* keys.
// Any header starting with these prefixes is injected.
// Example: "CF-,X-Flagr-"
InjectedContextHTTPHeaderPrefixes []string `env:"FLAGR_INJECTED_CONTEXT_HTTP_HEADER_PREFIXES" envDefault:"" envSeparator:","`
// ExposureBatchSize - maximum exposures per POST /exposures request.
ExposureBatchSize int `env:"FLAGR_EXPOSURE_BATCH_SIZE" envDefault:"100"`
/**
DBDriver and DBConnectionStr define how we can write and read flags data.
For databases, flagr supports sqlite3, mysql and postgres.
For read-only evaluation, flagr supports file and http.
Examples:
FLAGR_DB_DBDRIVER FLAGR_DB_DBCONNECTIONSTR
================= ===============================================================
"sqlite3" "/tmp/file.db"
"sqlite3" ":memory:"
"mysql" "root:@tcp(127.0.0.1:18100)/flagr?parseTime=true"
"postgres" "postgres://user:password@host:5432/flagr?sslmode=disable"
"json_file" "/tmp/flags.json" # (it automatically sets EvalOnlyMode=true)
"json_http" "https://example.com/flags.json" # (it automatically sets EvalOnlyMode=true)
*/
DBDriver string `env:"FLAGR_DB_DBDRIVER" envDefault:"sqlite3"`
DBConnectionStr string `env:"FLAGR_DB_DBCONNECTIONSTR" envDefault:"flagr.sqlite"`
// DBConnectionDebug controls whether to show the database connection debugging logs
// warning: it may log the credentials to the stdout
DBConnectionDebug bool `env:"FLAGR_DB_DBCONNECTION_DEBUG" envDefault:"true"`
// DBConnectionRetryAttempts controls how we are going to retry on db connection when start the flagr server
DBConnectionRetryAttempts uint `env:"FLAGR_DB_DBCONNECTION_RETRY_ATTEMPTS" envDefault:"9"`
DBConnectionRetryDelay time.Duration `env:"FLAGR_DB_DBCONNECTION_RETRY_DELAY" envDefault:"100ms"`
// CORSEnabled - enable CORS
CORSEnabled bool `env:"FLAGR_CORS_ENABLED" envDefault:"true"`
CORSAllowCredentials bool `env:"FLAGR_CORS_ALLOW_CREDENTIALS" envDefault:"true"`
CORSAllowedHeaders []string `env:"FLAGR_CORS_ALLOWED_HEADERS" envDefault:"Origin,Accept,Content-Type,X-Requested-With,Authorization,Time_Zone" envSeparator:","`
CORSAllowedMethods []string `env:"FLAGR_CORS_ALLOWED_METHODS" envDefault:"GET,POST,PUT,DELETE,PATCH" envSeparator:","`
CORSAllowedOrigins []string `env:"FLAGR_CORS_ALLOWED_ORIGINS" envDefault:"*" envSeparator:","`
CORSExposedHeaders []string `env:"FLAGR_CORS_EXPOSED_HEADERS" envDefault:"WWW-Authenticate" envSeparator:","`
CORSMaxAge int `env:"FLAGR_CORS_MAX_AGE" envDefault:"600"`
// SentryEnabled - enable Sentry and Sentry DSN
SentryEnabled bool `env:"FLAGR_SENTRY_ENABLED" envDefault:"false"`
SentryDSN string `env:"FLAGR_SENTRY_DSN" envDefault:""`
SentryEnvironment string `env:"FLAGR_SENTRY_ENVIRONMENT" envDefault:""`
// NewRelicEnabled - enable the NewRelic monitoring for all the endpoints and DB operations
NewRelicEnabled bool `env:"FLAGR_NEWRELIC_ENABLED" envDefault:"false"`
NewRelicDistributedTracingEnabled bool `env:"FLAGR_NEWRELIC_DISTRIBUTED_TRACING_ENABLED" envDefault:"false"`
NewRelicAppName string `env:"FLAGR_NEWRELIC_NAME" envDefault:"flagr"`
NewRelicKey string `env:"FLAGR_NEWRELIC_KEY" envDefault:""`
// StatsdEnabled - enable statsd metrics for all the endpoints and DB operations
StatsdEnabled bool `env:"FLAGR_STATSD_ENABLED" envDefault:"false"`
StatsdHost string `env:"FLAGR_STATSD_HOST" envDefault:"127.0.0.1"`
StatsdPort string `env:"FLAGR_STATSD_PORT" envDefault:"8125"`
StatsdPrefix string `env:"FLAGR_STATSD_PREFIX" envDefault:"flagr."`
StatsdAPMEnabled bool `env:"FLAGR_STATSD_APM_ENABLED" envDefault:"false"`
StatsdAPMPort string `env:"FLAGR_STATSD_APM_PORT" envDefault:"8126"`
StatsdAPMServiceName string `env:"FLAGR_STATSD_APM_SERVICE_NAME" envDefault:"flagr"`
// PrometheusEnabled - enable prometheus metrics export
PrometheusEnabled bool `env:"FLAGR_PROMETHEUS_ENABLED" envDefault:"false"`
// PrometheusPath - set the path on which prometheus metrics are available to scrape
PrometheusPath string `env:"FLAGR_PROMETHEUS_PATH" envDefault:"/metrics"`
// PrometheusIncludeLatencyHistogram - set whether Prometheus should also export a histogram of request latencies (this increases cardinality significantly)
PrometheusIncludeLatencyHistogram bool `env:"FLAGR_PROMETHEUS_INCLUDE_LATENCY_HISTOGRAM" envDefault:"false"`
// RecorderEnabled - master kill switch for all data recorders (including Datar)
RecorderEnabled bool `env:"FLAGR_RECORDER_ENABLED" envDefault:"false"`
// RecorderType - comma-separated list of recorders to enable, e.g. "kafka,datar"
RecorderType []string `env:"FLAGR_RECORDER_TYPE" envDefault:"kafka" envSeparator:","`
/**
RecorderFrameOutputMode - indicates which data record frame output mode should we use.
Possible values: payload_string, payload_raw_json
* payload_string mode:
it respects the encryption settings, and it will stringify the payload to unify
the type of the output for both plaintext and encrypted payload.
{"payload":"{\"evalContext\":{\"entityID\":\"123\"},\"flagID\":1,\"flagKey\":null,\"flagSnapshotID\":1,\"segmentID\":1,\"timestamp\":null,\"variantAttachment\":null,\"variantID\":1,\"variantKey\":\"control\"}","encrypted": false}
* payload_raw_json mode:
it ignores the encryption settings.
{"payload":{"evalContext":{"entityID":"123"},"flagID":1,"flagKey":null,"flagSnapshotID":1,"segmentID":1,"timestamp":null,"variantAttachment":null,"variantID":1,"variantKey":"control"}}
*/
RecorderFrameOutputMode string `env:"FLAGR_RECORDER_FRAME_OUTPUT_MODE" envDefault:"payload_string"`
// Kafka related configurations for data records logging (Flagr Metrics)
RecorderKafkaVersion string `env:"FLAGR_RECORDER_KAFKA_VERSION" envDefault:"0.8.2.0"`
RecorderKafkaBrokers string `env:"FLAGR_RECORDER_KAFKA_BROKERS" envDefault:":9092"`
RecorderKafkaCompressionCodec int8 `env:"FLAGR_RECORDER_KAFKA_COMPRESSION_CODEC" envDefault:"0"`
RecorderKafkaCertFile string `env:"FLAGR_RECORDER_KAFKA_CERTFILE" envDefault:""`
RecorderKafkaKeyFile string `env:"FLAGR_RECORDER_KAFKA_KEYFILE" envDefault:""`
RecorderKafkaCAFile string `env:"FLAGR_RECORDER_KAFKA_CAFILE" envDefault:""`
RecorderKafkaVerifySSL bool `env:"FLAGR_RECORDER_KAFKA_VERIFYSSL" envDefault:"false"`
RecorderKafkaSimpleSSL bool `env:"FLAGR_RECORDER_KAFKA_SIMPLE_SSL" envDefault:"false"`
RecorderKafkaSASLUsername string `env:"FLAGR_RECORDER_KAFKA_SASL_USERNAME" envDefault:""`
RecorderKafkaSASLPassword string `env:"FLAGR_RECORDER_KAFKA_SASL_PASSWORD" envDefault:""`
RecorderKafkaVerbose bool `env:"FLAGR_RECORDER_KAFKA_VERBOSE" envDefault:"true"`
RecorderKafkaTopic string `env:"FLAGR_RECORDER_KAFKA_TOPIC" envDefault:"flagr-records"`
RecorderKafkaPartitionKeyEnabled bool `env:"FLAGR_RECORDER_KAFKA_PARTITION_KEY_ENABLED" envDefault:"true"`
RecorderKafkaRetryMax int `env:"FLAGR_RECORDER_KAFKA_RETRYMAX" envDefault:"5"`
RecorderKafkaMaxOpenReqs int `env:"FLAGR_RECORDER_KAFKA_MAXOPENREQUESTS" envDefault:"5"`
RecorderKafkaRequiredAcks int `env:"FLAGR_RECORDER_KAFKA_REQUIRED_ACKS" envDefault:"1"` // 0: no response, 1: wait for local, -1: wait for all
RecorderKafkaIdempotent bool `env:"FLAGR_RECORDER_KAFKA_IDEMPOTENT" envDefault:"false"`
RecorderKafkaFlushFrequency time.Duration `env:"FLAGR_RECORDER_KAFKA_FLUSHFREQUENCY" envDefault:"500ms"`
RecorderKafkaEncrypted bool `env:"FLAGR_RECORDER_KAFKA_ENCRYPTED" envDefault:"false"`
RecorderKafkaEncryptionKey string `env:"FLAGR_RECORDER_KAFKA_ENCRYPTION_KEY" envDefault:""`
// Kinesis related configurations for data records logging (Flagr Metrics)
RecorderKinesisStreamName string `env:"FLAGR_RECORDER_KINESIS_STREAM_NAME" envDefault:"flagr-records"`
RecorderKinesisBacklogCount int `env:"FLAGR_RECORDER_KINESIS_BACKLOG_COUNT" envDefault:"500"`
RecorderKinesisMaxConnections int `env:"FLAGR_RECORDER_KINESIS_MAX_CONNECTIONS" envDefault:"24"`
RecorderKinesisFlushInterval time.Duration `env:"FLAGR_RECORDER_KINESIS_FLUSH_INTERVAL" envDefault:"5s"`
RecorderKinesisBatchCount int `env:"FLAGR_RECORDER_KINESIS_BATCH_COUNT" envDefault:"500"`
RecorderKinesisBatchSize int `env:"FLAGR_RECORDER_KINESIS_BATCH_SIZE" envDefault:"0"`
RecorderKinesisAggregateBatchCount int `env:"FLAGR_RECORDER_KINESIS_AGGREGATE_BATCH_COUNT" envDefault:"4294967295"`
RecorderKinesisAggregateBatchSize int `env:"FLAGR_RECORDER_KINESIS_AGGREGATE_BATCH_SIZE" envDefault:"51200"`
RecorderKinesisVerbose bool `env:"FLAGR_RECORDER_KINESIS_VERBOSE" envDefault:"false"`
// Pubsub related configurations for data records logging (Flagr Metrics)
RecorderPubsubProjectID string `env:"FLAGR_RECORDER_PUBSUB_PROJECT_ID" envDefault:""`
RecorderPubsubTopicName string `env:"FLAGR_RECORDER_PUBSUB_TOPIC_NAME" envDefault:"flagr-records"`
RecorderPubsubKeyFile string `env:"FLAGR_RECORDER_PUBSUB_KEYFILE" envDefault:""`
RecorderPubsubVerbose bool `env:"FLAGR_RECORDER_PUBSUB_VERBOSE" envDefault:"false"`
RecorderPubsubVerboseCancelTimeout time.Duration `env:"FLAGR_RECORDER_PUBSUB_VERBOSE_CANCEL_TIMEOUT" envDefault:"5s"`
// RecorderDatarFlushInterval - how often to flush in-memory aggregates to DB
RecorderDatarFlushInterval time.Duration `env:"FLAGR_RECORDER_DATAR_FLUSH_INTERVAL" envDefault:"60s"`
/**
JWTAuthEnabled enables the JWT Auth
Via Cookies:
The pattern of using JWT auth token using cookies is that it redirects to the URL to set cross subdomain cookie
For example, redirect to auth.example.com/signin, which sets Cookie access_token=jwt_token for domain
".example.com". One can also whitelist some routes so that they don't get blocked by JWT auth
Via Headers:
If you wish to use JWT Auth via headers you can simply set the header `Authorization Bearer [access_token]`
Supported signing methods:
* HS256/HS512, in this case `FLAGR_JWT_AUTH_SECRET` contains the passphrase
* RS256, in this case `FLAGR_JWT_AUTH_SECRET` contains the key in PEM Format
Note:
If the access_token is present in both the header and cookie only the latest will be used
*/
JWTAuthEnabled bool `env:"FLAGR_JWT_AUTH_ENABLED" envDefault:"false"`
JWTAuthDebug bool `env:"FLAGR_JWT_AUTH_DEBUG" envDefault:"false"`
JWTAuthPrefixWhitelistPaths []string `env:"FLAGR_JWT_AUTH_WHITELIST_PATHS" envDefault:"/api/v1/health,/api/v1/evaluation,/api/v1/exposures,/static" envSeparator:","`
JWTAuthExactWhitelistPaths []string `env:"FLAGR_JWT_AUTH_EXACT_WHITELIST_PATHS" envDefault:",/" envSeparator:","`
JWTAuthCookieTokenName string `env:"FLAGR_JWT_AUTH_COOKIE_TOKEN_NAME" envDefault:"access_token"`
JWTAuthSecret string `env:"FLAGR_JWT_AUTH_SECRET" envDefault:""`
JWTAuthNoTokenStatusCode int `env:"FLAGR_JWT_AUTH_NO_TOKEN_STATUS_CODE" envDefault:"307"` // "307" or "401"
JWTAuthNoTokenRedirectURL string `env:"FLAGR_JWT_AUTH_NO_TOKEN_REDIRECT_URL" envDefault:""`
JWTAuthUserProperty string `env:"FLAGR_JWT_AUTH_USER_PROPERTY" envDefault:"flagr_user"`
// JWTAuthUserClaim can be used as the indicator of a user for created_by or updated_by.
// E.g. sub, email, user, name, and etc in a JWT token.
JWTAuthUserClaim string `env:"FLAGR_JWT_AUTH_USER_CLAIM" envDefault:"sub"`
// "HS256" and "RS256" supported
JWTAuthSigningMethod string `env:"FLAGR_JWT_AUTH_SIGNING_METHOD" envDefault:"HS256"`
// Identify users through headers
HeaderAuthEnabled bool `env:"FLAGR_HEADER_AUTH_ENABLED" envDefault:"false"`
HeaderAuthUserField string `env:"FLAGR_HEADER_AUTH_USER_FIELD" envDefault:"X-Email"`
// Identify users through cookies
// E.g. via cloudflare zero trust, we derive the user email from the JWT token stored in the cookie of CF_Authorization
CookieAuthEnabled bool `env:"FLAGR_COOKIE_AUTH_ENABLED" envDefault:"false"`
CookieAuthUserField string `env:"FLAGR_COOKIE_AUTH_USER_FIELD" envDefault:"CF_Authorization"`
CookieAuthUserFieldJWTClaim string `env:"FLAGR_COOKIE_AUTH_USER_FIELD_JWT_CLAIM" envDefault:"email"`
// Authenticate with basic auth
BasicAuthEnabled bool `env:"FLAGR_BASIC_AUTH_ENABLED" envDefault:"false"`
BasicAuthUsername string `env:"FLAGR_BASIC_AUTH_USERNAME" envDefault:""`
BasicAuthPassword string `env:"FLAGR_BASIC_AUTH_PASSWORD" envDefault:""`
BasicAuthPrefixWhitelistPaths []string `env:"FLAGR_BASIC_AUTH_WHITELIST_PATHS" envDefault:"/api/v1/health,/api/v1/flags,/api/v1/evaluation,/api/v1/exposures" envSeparator:","`
BasicAuthExactWhitelistPaths []string `env:"FLAGR_BASIC_AUTH_EXACT_WHITELIST_PATHS" envDefault:"" envSeparator:","`
// ===== Notification - Global Settings =====
// NotificationDetailedDiffEnabled - notify detailed diff of pre and post values
NotificationDetailedDiffEnabled bool `env:"FLAGR_NOTIFICATION_DETAILED_DIFF_ENABLED" envDefault:"false"`
// NotificationTimeout - timeout for sending notifications
NotificationTimeout time.Duration `env:"FLAGR_NOTIFICATION_TIMEOUT" envDefault:"10s"`
// NotificationMaxRetries - maximum number of retry attempts for HTTP notifications
NotificationMaxRetries int `env:"FLAGR_NOTIFICATION_MAX_RETRIES" envDefault:"3"`
// NotificationRetryBase - base delay for exponential backoff with jitter
NotificationRetryBase time.Duration `env:"FLAGR_NOTIFICATION_RETRY_BASE" envDefault:"1s"`
// NotificationRetryMax - maximum delay between retries
NotificationRetryMax time.Duration `env:"FLAGR_NOTIFICATION_RETRY_MAX" envDefault:"10s"`
// ===== Notification - Webhook Provider =====
// NotificationWebhookEnabled - enable generic webhook notifications
NotificationWebhookEnabled bool `env:"FLAGR_NOTIFICATION_WEBHOOK_ENABLED" envDefault:"false"`
// NotificationWebhookURL - Webhook URL for generic notifications
NotificationWebhookURL string `env:"FLAGR_NOTIFICATION_WEBHOOK_URL" envDefault:""`
// NotificationWebhookHeaders - Webhook Headers for generic notifications, e.g. "Authorization: Bearer token,X-Custom-Header: value"
NotificationWebhookHeaders string `env:"FLAGR_NOTIFICATION_WEBHOOK_HEADERS" envDefault:""`
// WebPrefix - base path for web and API
// e.g. FLAGR_WEB_PREFIX=/foo
// UI path => localhost:18000/foo"
// API path => localhost:18000/foo/api/v1"
WebPrefix string `env:"FLAGR_WEB_PREFIX" envDefault:""`
}{}Quick start
The fastest path to a running server is the Self-hosting guide, which covers Docker, Compose, and Kubernetes. If you just want the minimal environment for a MySQL-backed server, copy these four variables and go:
export HOST=0.0.0.0
export PORT=18000
export FLAGR_DB_DBDRIVER=mysql
export FLAGR_DB_DBCONNECTIONSTR='user:pass@tcp(127.0.0.1:3306)/flagr?parseTime=true'If you'd rather serve flags from a static JSON file or URL with no database at all, set FLAGR_DB_DBDRIVER to json_file or json_http. That puts the server into eval-only mode automatically - see behavioral contracts - eval-only and the JSON flag source spec.
Guide
Server & HTTP
How the process binds, what it serves, and what it logs. Defaults are fine for local ./flagr; containers should set HOST=0.0.0.0 (the official Dockerfile already does).
| Variable | Default | Notes |
|---|---|---|
HOST / PORT | localhost / 18000 | Bind address (env.go); Docker image sets HOST=0.0.0.0 |
FLAGR_WEB_PREFIX | (empty) | UI + API base path |
FLAGR_UI_ENABLED | true | false = API-only |
FLAGR_LOGRUS_LEVEL / FORMAT | info / text | Use json in production |
FLAGR_PPROF_ENABLED | true | pprof endpoints |
FLAGR_MIDDLEWARE_VERBOSE_LOGGER_* | on | Exclude hot paths via …_EXCLUDE_URLS |
FLAGR_MIDDLEWARE_GZIP_ENABLED | true |
CORS lives under FLAGR_CORS_*, enabled by default with permissive origins. Full list is in the source block; tighten it only for browser-facing lockdowns.
Evaluation & cache
Evaluation never hits the database on the hot path. It reads an in-memory EvalCache rebuilt on a fixed interval.
| Variable | Default | Notes |
|---|---|---|
FLAGR_EVALCACHE_REFRESHINTERVAL | 3s | EvalCache reload period |
FLAGR_EVALCACHE_REFRESHTIMEOUT | 59s | Single fetch timeout |
FLAGR_EVAL_DEBUG_ENABLED | true | + enableDebug on request → segment logs (Debug console) |
FLAGR_EVAL_BATCH_SIZE | 0 | 0 = unlimited batch eval (POST and GET batch) |
FLAGR_EVAL_GET_MAX_URL_BYTES | 8192 | GET json= raw query cap; 0 = off - use cases |
FLAGR_EXPOSURE_BATCH_SIZE | 100 | Max rows per POST /exposures |
After a flag change, variantKey can stay blank or stale until the next reload. That lag is a contract, not a bug. See EvalCache freshness. Automated tests should wait at least one interval (this repo uses waitForEvalReady).
Eval-only is the usual product path when FLAGR_DB_DBDRIVER is json_file or json_http (setupEvalOnlyMode in pkg/config/config.go). FLAGR_EVAL_ONLY_MODE=true can be set on other drivers as an edge case; prefer JSON drivers for eval-edge deploys. The UI runs as a read-only browser in this mode (GET /health reports evalOnlyMode: true; write APIs return 403). Surface: behavioral contracts: eval-only.
Built-in context injection
| Variable | Default | Notes |
|---|---|---|
FLAGR_INJECTED_CONTEXT_ENABLED | false | Merge @ts* and @http_* into entityContext before eval |
FLAGR_INJECTED_CONTEXT_HTTP_HEADERS | "" | Comma-separated headers → @http_* keys |
FLAGR_INJECTED_CONTEXT_HTTP_HEADER_PREFIXES | "" | Prefix match (e.g. CF- for Cloudflare) |
Full guide: Built-in context injection.
Eval cache export
A running server can dump its in-memory cache as JSON via GET /api/v1/export/eval_cache/json, with optional enabled, ids, keys, tags, and tagsOperator (ANY / ALL) query parameters.
Database
Two variables decide where flags live: the driver and the connection string. Defaults are local SQLite; production typically uses MySQL or Postgres. JSON drivers load flags from a file or URL for read-only eval.
| Variable | Default |
|---|---|
FLAGR_DB_DBDRIVER | sqlite3 |
FLAGR_DB_DBCONNECTIONSTR | flagr.sqlite |
| Driver | Role |
|---|---|
sqlite3 | Local dev (default) |
mysql / postgres | Production |
json_file / json_http | Flags from file or URL (JSON spec) |
Authentication
Authentication is off by default, so a freshly started server is open until you turn something on. Flagr supports two layers that can be used independently: basic auth, which guards the UI, and JWT auth, which guards the API. Both layers let you whitelist paths so hot evaluation traffic doesn't have to authenticate - and both ship with defaults that leave /api/v1/evaluation and /api/v1/exposures open, so turning auth on won't break your integration.
A minimal basic-auth setup is three variables:
FLAGR_BASIC_AUTH_ENABLED=true
FLAGR_BASIC_AUTH_USERNAME=admin
FLAGR_BASIC_AUTH_PASSWORD=passwordJWT is richer. The variables cover enabling it (FLAGR_JWT_AUTH_ENABLED), the shared secret or PEM key (FLAGR_JWT_AUTH_SECRET), the signing method (HS256 / HS512 / RS256), and a set of prefix and exact whitelist paths. All of them are in the source above. JWT tokens can arrive by cookie or by Authorization: Bearer header; when both are present, the header wins.
Prefix whitelist matching (JWT and basic) uses util.HasSafePrefix, which calls util.HasDotDot. A .. path segment (including %2e%2e / %252e%252e and ..\) never matches a whitelist prefix. Independently, rejectDotDotPath rejects those paths with 401 before auth or the eval-only flags deny. behavioral contracts: eval-only.
Separately, Flagr can identify who made a mutation for audit logging without doing full authentication. FLAGR_HEADER_AUTH_* reads a user identifier from a header (handy behind a corporate proxy), and FLAGR_COOKIE_AUTH_* reads one from a cookie (handy behind something like Cloudflare Zero Trust). These stamp created_by / updated_by on changes; they don't gate access.
One thing worth calling out: the default JWT whitelist allows unauthenticated exposure logging. If the integrity of your impression stream matters, narrow the whitelist to lock down /api/v1/exposures and rate-limit it at the edge. The Exposure logging page walks through the tradeoffs.
Data recorders
Recording gates (master switch, recorder type, per-flag dataRecordsEnabled): behavioral contracts: recording gates. Blank assignment vs whether a row is written: blank vs stream.
FLAGR_RECORDER_TYPE is a comma-separated list so you can combine recorders.
FLAGR_RECORDER_TYPE | Doc |
|---|---|
kafka, kinesis, pubsub | Eval + exposure stream - Recorders & A/B |
datar | In-process eval counts only - Datar (no exposures) |
Streaming recorders ship eval and exposure rows to a broker; Datar keeps in-process evaluation counts and flushes them to the DB. Combining kafka,datar is common: live stream plus cheap dashboards. FLAGR_RECORDER_FRAME_OUTPUT_MODE: payload_string stringifies the payload (and respects encryption); payload_raw_json embeds the object (and ignores encryption).
The minimal Kafka setup is four variables:
FLAGR_RECORDER_ENABLED=true
FLAGR_RECORDER_TYPE=kafka
FLAGR_RECORDER_KAFKA_BROKERS=kafka1:9092
FLAGR_RECORDER_KAFKA_TOPIC=flagr-recordsEverything else under FLAGR_RECORDER_* - broker TLS and SASL, compression, Kinesis batch tuning, Pub/Sub credentials - is in the source above. Those knobs exist for production hardening; the defaults are meant to get a row onto a topic, not to survive a misconfigured cluster.
Webhooks
Flagr can fire a webhook whenever a flag changes, which is how teams wire approvals, audit trails, or cache invalidation downstream. The webhook provider is one part of the notification system: you enable it, point it at a URL, give it headers, and it retries with exponential backoff. There's also a toggle for detailed diffs, so the payload can include exactly which fields changed before and after. The full variable set and the retry semantics are on the Notifications page.
Observability
The last group is how you watch the server once it's running. Flagr exports metrics in three shapes - Prometheus scrape, Statsd push, and two hosted APMs - and you typically pick one rather than stacking them.
| Area | Switch |
|---|---|
| Prometheus | FLAGR_PROMETHEUS_ENABLED, FLAGR_PROMETHEUS_PATH |
| Statsd | FLAGR_STATSD_ENABLED, host/port/prefix |
| Sentry / New Relic | FLAGR_SENTRY_ENABLED, FLAGR_NEWRELIC_ENABLED |
Prometheus is the default choice for Kubernetes; Statsd suits traditional infrastructure; Sentry and New Relic are for error tracking and distributed tracing respectively. Each family has its own tuning variables in the source above - latency histograms for Prometheus, APM ports for Statsd, DSNs and app names for the hosted services.
Maintaining this page
When you add or change variables in pkg/config/env.go, update the guide tables here only if operators need a one-line summary. The embedded source is copied from pkg/config/env.go by make build-docs / make serve-docs into docs/snippets/env.go at build time.
