Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration

Fluree server is configured via a configuration file, command-line flags, and environment variables.

Configuration Methods

Configuration File (TOML, JSON, or JSON-LD)

The server reads configuration from .fluree/config.toml (or .fluree/config.jsonld) — the same file used by the Fluree CLI. Server settings live under the [server] section (or "server" key in JSON/JSON-LD). The server walks up from the current working directory looking for .fluree/config.toml or .fluree/config.jsonld, falling back to the global Fluree config directory ($FLUREE_HOME, or the platform config directory — see table below).

Global Directory Layout

When $FLUREE_HOME is set, both config and data share that single directory. When it is not set, the platform’s config and data directories are used:

ContentLinuxmacOSWindows
Config (config.toml)~/.config/fluree~/Library/Application Support/fluree%LOCALAPPDATA%\fluree
Data (storage/, active)~/.local/share/fluree~/Library/Application Support/fluree%LOCALAPPDATA%\fluree

On Linux, config and data directories are separated per the XDG Base Directory specification. On macOS and Windows both resolve to the same directory. When directories are split, fluree init --global writes an absolute storage_path into config.toml so the server can locate the data directory regardless of working directory.

# Use default config file discovery
fluree-server

# Override config file path
fluree-server --config /etc/fluree/config.toml

# Activate a profile
fluree-server --profile prod

Example config.toml:

[server]
listen_addr = "0.0.0.0:8090"
storage_path = "/var/lib/fluree"
log_level = "info"
query_timeout_ms = 900000  # 15 minutes; set to 0 to disable
query_min_t_timeout_ms = 5000
# cache_max_mb = 4096  # global in-memory cache budget (MB); default: tiered by RAM (<4GB: 30%, 4-8GB: 40%, >=8GB: 35%)
# disk_cache_max_mb = 20480  # global on-disk cache budget (MB), shared across object storage + Iceberg; default: auto-detect; 0 disables

[server.query_refresh]
enabled = false
ttl_ms = 1000

[server.indexing]
enabled = true
reindex_min_bytes = 100
# reindex_max_bytes defaults to 20% of system RAM; override only if needed:
# reindex_max_bytes = 536870912  # 512 MB

[server.auth.data]
mode = "required"
trusted_issuers = ["did:key:z6Mk..."]

JSON is also supported (detected by .json file extension):

{
  "server": {
    "listen_addr": "0.0.0.0:8090",
    "storage_path": "/var/lib/fluree",
    "indexing": { "enabled": true }
  }
}

JSON-LD Format

JSON-LD config files (.jsonld extension) add a @context that maps config keys to the Fluree config vocabulary (https://ns.flur.ee/config#), making the file valid JSON-LD. Generate one with:

fluree init --format jsonld

Example .fluree/config.jsonld:

{
  "@context": {
    "@vocab": "https://ns.flur.ee/config#"
  },
  "_comment": "Fluree Configuration — JSON-LD format.",
  "server": {
    "listen_addr": "0.0.0.0:8090",
    "storage_path": ".fluree/storage",
    "log_level": "info",
    "indexing": {
      "enabled": true,
      "reindex_min_bytes": 100
    }
  },
  "profiles": {
    "prod": {
      "server": {
        "log_level": "warn"
      }
    }
  }
}

The @context is validated at load time (using the JSON-LD parser) but does not affect config value resolution — serde ignores unknown keys like @context and _comment. If both config.toml and config.jsonld exist in the same directory, TOML takes precedence and a warning is logged.

Profiles

Profiles allow environment-specific overrides. Define them in [profiles.<name>.server] and activate with --profile <name>:

[server]
log_level = "info"

[profiles.dev.server]
log_level = "debug"

[profiles.prod.server]
log_level = "warn"
[profiles.prod.server.indexing]
enabled = true
[profiles.prod.server.auth.data]
mode = "required"

Profile values are deep-merged onto [server] — only the fields present in the profile are overridden.

Command-Line Flags

fluree-server \
  --listen-addr 0.0.0.0:8090 \
  --storage-path /var/lib/fluree \
  --log-level info

Environment Variables

All CLI flags have corresponding environment variables with FLUREE_ prefix:

export FLUREE_LISTEN_ADDR=0.0.0.0:8090
export FLUREE_STORAGE_PATH=/var/lib/fluree
export FLUREE_LOG_LEVEL=info

fluree-server

A few operational knobs are environment-only (no CLI flag):

VariableDefaultPurpose
FLUREE_REASONING_MAX_FACTS1,000,000Server-wide default OWL2-RL materialization budget (max derived facts). Overridden per ledger by f:reasoningMaxFacts and per query by "reasoningBudget"; see Reasoning.
FLUREE_REASONING_MAX_SECONDS30Server-wide default OWL2-RL materialization budget (wall-clock seconds). Same override chain as above.

Precedence

Configuration precedence (highest to lowest):

  1. Command-line flags
  2. Environment variables
  3. Profile overrides ([profiles.<name>.server])
  4. Config file ([server])
  5. Built-in defaults

Error Handling

If --config or --profile is specified and the configuration cannot be loaded (file not found, parse error, missing profile), the server exits with an error. This prevents silent misconfiguration in production.

If the config file is auto-discovered (no explicit --config) and cannot be parsed, the server logs a warning and continues with CLI/env/default values only.

Server Configuration

Listen Address

Address and port to bind to:

FlagEnv VarDefault
--listen-addrFLUREE_LISTEN_ADDR0.0.0.0:8090
fluree-server --listen-addr 0.0.0.0:9090

Storage Path

Path for file-based storage. If not specified, defaults to .fluree/storage relative to the working directory (the same location used by fluree init):

FlagEnv VarDefault
--storage-pathFLUREE_STORAGE_PATH.fluree/storage
# Explicit storage path (e.g. production)
fluree-server --storage-path /var/lib/fluree

# Default: uses .fluree/storage in the working directory
fluree-server

Connection Configuration (S3, DynamoDB, etc.)

For storage backends beyond local files — S3, DynamoDB nameservice, split commit/index storage, encryption — use a JSON-LD connection config file:

FlagEnv VarDefault
--connection-configFLUREE_CONNECTION_CONFIGNone

When set, the server builds its storage and nameservice from the connection config file instead of using --storage-path. The file uses the same JSON-LD format as the Fluree API connection config.

# S3 + DynamoDB via connection config
fluree server run --connection-config /etc/fluree/connection.jsonld

# Or via environment variable
FLUREE_CONNECTION_CONFIG=/etc/fluree/connection.jsonld fluree server run

Example connection config (connection.jsonld):

{
  "@context": {
    "@base": "https://ns.flur.ee/config/connection/",
    "@vocab": "https://ns.flur.ee/system#"
  },
  "@graph": [
    {
      "@id": "commitStorage",
      "@type": "Storage",
      "s3Bucket": "fluree-commits",
      "s3Prefix": "fluree-data/"
    },
    {
      "@id": "indexStorage",
      "@type": "Storage",
      "s3Bucket": "fluree-indexes--use1-az4--x-s3"
    },
    {
      "@id": "publisher",
      "@type": "Publisher",
      "dynamodbTable": "fluree-nameservice",
      "dynamodbRegion": "us-east-1"
    },
    {
      "@id": "conn",
      "@type": "Connection",
      "commitStorage": { "@id": "commitStorage" },
      "indexStorage": { "@id": "indexStorage" },
      "primaryPublisher": { "@id": "publisher" }
    }
  ]
}

Behavior notes:

  • --connection-config and --storage-path are mutually exclusive. If both are set, --connection-config takes precedence (a warning is logged).
  • Server-level settings (--cache-max-mb, --indexing-enabled, --reindex-min-bytes, --reindex-max-bytes) override any equivalent values from the connection config.
  • --indexing-enabled defaults to true. Pass --indexing-enabled=false only when a separate peer/indexer process owns index maintenance for the same storage.
  • AWS credentials and region are resolved via the standard AWS SDK chain (env vars, instance profile, ~/.aws/config, etc.) — they are not part of the connection config.
  • The connection config can use envVar indirection for sensitive fields like S3 bucket names or encryption keys (see ConfigurationValue).

Config file equivalent:

[server]
connection_config = "/etc/fluree/connection.jsonld"

Capabilities by Backend

Not all nameservice backends support all features. The server checks capabilities at runtime:

FeatureFile (local)DynamoDBStorage-backed
Query / transactYesYesYes
Event subscriptionsYesNoNo
Default context (read)YesYesYes
Default context (write)YesYesNo

If a capability is not available, the server returns an appropriate error (e.g., 501 for event subscriptions with DynamoDB).

CORS

Enable Cross-Origin Resource Sharing:

FlagEnv VarDefault
--cors-enabledFLUREE_CORS_ENABLEDtrue

When enabled, allows requests from any origin.

Body Limit

Maximum request body size in bytes:

FlagEnv VarDefault
--body-limitFLUREE_BODY_LIMIT52428800 (50MB)

Query Timeout

Maximum query execution time in milliseconds. The server starts a timeout task that signals query cancellation when the limit elapses; query execution observes that signal at I/O boundaries — operator batch handoffs, leaflet refills in the fused COUNT fast paths, and parallel-partition starts — never inside per-row loops (in-loop checks measurably perturb hot-loop codegen). Cancellation latency is bounded by one leaflet/batch of work, typically well under 10ms. Set to 0 to disable the server-side timeout. If an HTTP client disconnects while a query is still running, the server signals cancellation through the same cooperative handle so long-running operators can stop at the next checkpoint.

FlagEnv VarDefault
--query-timeout-msFLUREE_QUERY_TIMEOUT_MS900000 (15 minutes)
--query-min-t-timeout-msFLUREE_QUERY_MIN_T_TIMEOUT_MS5000 (5 seconds)
--stream-heartbeat-msFLUREE_STREAM_HEARTBEAT_MS15000 (15 seconds)

query_min_t_timeout_ms caps HTTP read-after-write waits from Fluree-Min-T, opts.min-t, and numeric @t snapshot pins. It remains enforced even when query_timeout_ms = 0.

stream_heartbeat_ms is the keep-alive cadence for the streaming query endpoint (/stream/query). Records flush at this interval during stalls so a long-running query survives a fronting proxy’s idle timeout; set it below that timeout (e.g. under CloudFront/ALB’s ~60s). 0 disables heartbeats.

Query-Time Refresh

Long-running query servers can opt in to a bounded nameservice freshness check before current-head queries. When enabled, the server calls the same Fluree::refresh() API used by serverless query handlers, but gates the call by a per-process, per-ledger TTL so high-QPS traffic does not check DynamoDB on every request.

This is demand-driven, not a background poller: the first current-head query for a ledger after its TTL window expires pays the nameservice round-trip, and idle ledgers are not refreshed.

This is useful for split deployments where writers update a shared DynamoDB nameservice but query servers do not subscribe to a transaction server’s SSE event stream.

For a hard read-after-write guarantee when the client knows the transaction t, send Fluree-Min-T (or JSON-LD opts.min-t) on the query request. TTL refresh bounds ordinary staleness; min-t waits for a specific transaction time.

FlagEnv VarDefaultDescription
--query-refresh-enabledFLUREE_QUERY_REFRESH_ENABLEDfalseEnable TTL-gated nameservice refresh before current-head query execution
--query-refresh-ttl-msFLUREE_QUERY_REFRESH_TTL_MS1000Minimum interval between refresh checks for the same ledger in one server process; set 0 to check every request

Config file equivalent:

[server.query_refresh]
enabled = true
ttl_ms = 200

Log Level

Logging verbosity:

FlagEnv VarDefault
--log-levelFLUREE_LOG_LEVELinfo

Options: trace, debug, info, warn, error

Cache Size

Global cache budget (MB):

FlagEnv VarDefault
--cache-max-mbFLUREE_CACHE_MAX_MBTiered by RAM: <4GB: 30%, 4-8GB: 40%, >=8GB: 35%
--disk-cache-max-mbFLUREE_DISK_CACHE_MAX_MBAuto-detect from free disk; 0 disables. Shared across object storage + Iceberg

Background Indexing

Enable background indexing and configure novelty backpressure thresholds:

FlagEnv VarDefaultDescription
--indexing-enabledFLUREE_INDEXING_ENABLEDtrueEnable background indexing (set false only when an external indexer process owns this storage)
--reindex-min-bytesFLUREE_REINDEX_MIN_BYTES100Soft threshold (triggers background indexing; default ≈ reindex every commit)
--reindex-max-bytesFLUREE_REINDEX_MAX_BYTES20% of system RAM (256 MB fallback)Hard threshold (blocks commits until reindexed)

Config file equivalent:

[server.indexing]
enabled = true
reindex_min_bytes = 100            # ≈ every commit — soft trigger
# reindex_max_bytes = 536870912    # 512 MB — defaults to 20% of system RAM if omitted

Server Role Configuration

Server Role

Operating mode: transaction server or query peer:

FlagEnv VarDefault
--server-roleFLUREE_SERVER_ROLEtransaction

Options:

  • transaction: Write-enabled, produces events stream
  • peer: Read-only, subscribes to transaction server

Transaction Server URL (Peer Mode)

Base URL of the transaction server (required in peer mode):

FlagEnv Var
--tx-server-urlFLUREE_TX_SERVER_URL
fluree-server \
  --server-role peer \
  --tx-server-url http://tx.internal:8090

Authentication Configuration

Replication vs Query Access

Fluree enforces a hard boundary between replication-scoped and query-scoped access:

  • Replication (fluree.storage.*): Raw commit and index block transfer for peer sync and CLI fetch/pull/push. These operations bypass dataset policy (data must be bit-identical). Replication tokens are operator/service-account credentials — never issue them to end users.
  • Query (fluree.ledger.read/write.*): Application-level data access through the query engine with full dataset policy enforcement. Query tokens are appropriate for end users and application service accounts.

A user holding only query-scoped tokens cannot clone or pull a ledger. They can fluree track a remote ledger (forwarding queries/transactions to the server) but cannot replicate its storage locally.

Events Endpoint Authentication

Protect the /v1/fluree/events SSE endpoint:

FlagEnv VarDefault
--events-auth-modeFLUREE_EVENTS_AUTH_MODEnone
--events-auth-audienceFLUREE_EVENTS_AUTH_AUDIENCENone
--events-auth-trusted-issuerFLUREE_EVENTS_AUTH_TRUSTED_ISSUERSNone

Modes:

  • none: No authentication
  • optional: Accept tokens but don’t require them
  • required: Require valid Bearer token

Supports both Ed25519 (embedded JWK) and OIDC/JWKS (RS256) tokens when the oidc feature is enabled and --jwks-issuer is configured. For OIDC tokens, issuer trust is implicit — only tokens signed by keys from configured JWKS endpoints will verify. For Ed25519 tokens, the issuer must appear in --events-auth-trusted-issuer.

# Ed25519 tokens only
fluree-server \
  --events-auth-mode required \
  --events-auth-trusted-issuer did:key:z6Mk...

# OIDC + Ed25519 (both work simultaneously)
fluree-server \
  --events-auth-mode required \
  --jwks-issuer "https://auth.example.com=https://auth.example.com/.well-known/jwks.json" \
  --events-auth-trusted-issuer did:key:z6Mk...

Data API Authentication

Protect query/transaction endpoints (including /v1/fluree/query/{ledger...}, /v1/fluree/insert/{ledger...}, /v1/fluree/upsert/{ledger...}, /v1/fluree/update/{ledger...}, /v1/fluree/info/{ledger...}, and /v1/fluree/exists/{ledger...}):

FlagEnv VarDefault
--data-auth-modeFLUREE_DATA_AUTH_MODEnone
--data-auth-audienceFLUREE_DATA_AUTH_AUDIENCENone
--data-auth-trusted-issuerFLUREE_DATA_AUTH_TRUSTED_ISSUERSNone
--data-auth-default-policy-classFLUREE_DATA_AUTH_DEFAULT_POLICY_CLASSNone

Modes:

  • none: No authentication (default)
  • optional: Accept tokens but don’t require them (development only)
  • required: Require either a valid Bearer token or a signed request (JWS/VC)

Bearer token scopes:

  • Read: fluree.ledger.read.all=true or fluree.ledger.read.ledgers=[...]
  • Write: fluree.ledger.write.all=true or fluree.ledger.write.ledgers=[...]

Back-compat: fluree.storage.* claims imply read scope for data endpoints.

fluree-server \
  --data-auth-mode required \
  --data-auth-trusted-issuer did:key:z6Mk...

OIDC / JWKS Token Verification

When the oidc feature is enabled, the server can verify JWT tokens signed by external identity providers (e.g., Fluree Cloud Service) using JWKS (JSON Web Key Set) endpoints. This is in addition to the existing embedded-JWK (Ed25519 did:key) verification path.

Dual-path dispatch: The server inspects each Bearer token’s header:

  • Embedded JWK (Ed25519): Uses the existing verify_jws() path — no JWKS needed.
  • kid header (RS256): Uses OIDC/JWKS path — fetches the signing key from the issuer’s JWKS endpoint.

Both paths coexist; no configuration change is needed for existing Ed25519 tokens.

FlagEnv VarDefaultDescription
--jwks-issuerFLUREE_JWKS_ISSUERSNoneOIDC issuer to trust (repeatable)
--jwks-cache-ttlFLUREE_JWKS_CACHE_TTL300JWKS cache TTL in seconds

The --jwks-issuer flag takes the format <issuer_url>=<jwks_url>:

fluree-server \
  --data-auth-mode required \
  --jwks-issuer "https://solo.example.com=https://solo.example.com/.well-known/jwks.json"

For multiple issuers, repeat the flag or use comma separation in the env var:

# CLI flags (repeatable)
fluree-server \
  --jwks-issuer "https://issuer1.example.com=https://issuer1.example.com/.well-known/jwks.json" \
  --jwks-issuer "https://issuer2.example.com=https://issuer2.example.com/.well-known/jwks.json"

# Environment variable (comma-separated)
export FLUREE_JWKS_ISSUERS="https://issuer1.example.com=https://issuer1.example.com/.well-known/jwks.json,https://issuer2.example.com=https://issuer2.example.com/.well-known/jwks.json"

Behavior details:

  • JWKS endpoints are fetched at startup (warm()) but the server starts even if they’re unreachable.
  • Keys are cached and refreshed when a kid miss occurs (rate-limited to one refresh per issuer every 10 seconds).
  • The token’s iss claim must exactly match a configured issuer URL — unconfigured issuers are rejected immediately with a clear error.
  • Data API, events, admin, and storage proxy endpoints all support JWKS verification. A single --jwks-issuer flag enables OIDC tokens across all endpoint groups. MCP auth continues to use the existing Ed25519 path only.

Connection-Scoped SPARQL Scope Enforcement

When a Bearer token is present for connection-scoped SPARQL queries (/v1/fluree/query with Content-Type: application/sparql-query), the server enforces ledger scope:

  • FROM / FROM NAMED clauses are parsed to extract ledger IDs (name:branch).
  • Each ledger ID is checked against the token’s read scope (fluree.ledger.read.all or fluree.ledger.read.ledgers).
  • Out-of-scope ledgers return 404 (no existence leak).
  • If no FROM clause is present, the query proceeds normally (the engine handles missing dataset errors).

Admin Endpoint Authentication

Protect /v1/fluree/create, /v1/fluree/drop, /v1/fluree/reindex, branch administration, and Iceberg mapping endpoints:

FlagEnv VarDefault
--admin-auth-modeFLUREE_ADMIN_AUTH_MODEnone
--admin-auth-trusted-issuerFLUREE_ADMIN_AUTH_TRUSTED_ISSUERSNone

Modes:

  • none: No authentication (development)
  • required: Require valid Bearer token (production)

Supports both Ed25519 (embedded JWK) and OIDC/JWKS (RS256) tokens when the oidc feature is enabled and --jwks-issuer is configured. For OIDC tokens, issuer trust is implicit — only tokens signed by keys from configured JWKS endpoints will verify. For Ed25519 tokens, the issuer must appear in --admin-auth-trusted-issuer or the fallback --events-auth-trusted-issuer.

# Ed25519 tokens only
fluree-server \
  --admin-auth-mode required \
  --admin-auth-trusted-issuer did:key:z6Mk...

# OIDC (trust comes from --jwks-issuer, no did:key issuers needed)
fluree-server \
  --admin-auth-mode required \
  --jwks-issuer "https://auth.example.com=https://auth.example.com/.well-known/jwks.json"

If no admin-specific issuers are configured, falls back to --events-auth-trusted-issuer.

MCP Endpoint

Protect and tune the /mcp Model Context Protocol endpoint:

FlagEnv VarDefault
--mcp-enabledFLUREE_MCP_ENABLEDfalse
--mcp-auth-trusted-issuerFLUREE_MCP_AUTH_TRUSTED_ISSUERSNone
--mcp-agent-json-max-bytesFLUREE_MCP_AGENT_JSON_MAX_BYTES32768
--mcp-query-timeout-msFLUREE_MCP_QUERY_TIMEOUT_MS300000 (5 minutes)

--mcp-agent-json-max-bytes (config file: [server.mcp] agent_json_max_bytes) is the byte budget for the MCP sparql_query tool’s Agent JSON result. Results larger than this are truncated and the envelope sets hasMore: true; an agent paginates by re-running with the returned t, an ORDER BY, and OFFSET advanced by the returned rowCount.

--mcp-query-timeout-ms (config file: [server.mcp] query_timeout_ms) is the server-side timeout for MCP sparql_query execution. It uses the same cooperative cancellation mechanism as HTTP queries, but defaults lower because MCP tool calls are usually interactive. Set to 0 to disable the MCP timeout. This setting does not apply to get_data_model, which may perform schema/stat collection without this query timeout.

fluree-server \
  --mcp-enabled \
  --mcp-auth-trusted-issuer did:key:z6Mk...

Peer Mode Configuration

Peer Subscription

Configure what the peer subscribes to:

FlagDescription
--peer-subscribe-allSubscribe to all ledgers and graph sources
--peer-ledger <ledger-id>Subscribe to specific ledger (repeatable)
--peer-graph-source <ledger-id>Subscribe to specific graph source (repeatable)
fluree-server \
  --server-role peer \
  --tx-server-url http://tx:8090 \
  --peer-subscribe-all

Or subscribe to specific resources:

fluree-server \
  --server-role peer \
  --tx-server-url http://tx:8090 \
  --peer-ledger books:main \
  --peer-ledger users:main

Peer Events Configuration

FlagEnv VarDescription
--peer-events-urlFLUREE_PEER_EVENTS_URLCustom events URL (default: {tx_server_url}/v1/fluree/events)
--peer-events-tokenFLUREE_PEER_EVENTS_TOKENBearer token for events (supports @filepath)

Peer Reconnection

FlagDefaultDescription
--peer-reconnect-initial-ms1000Initial reconnect delay
--peer-reconnect-max-ms30000Maximum reconnect delay
--peer-reconnect-multiplier2.0Backoff multiplier

Peer Storage Access

FlagEnv VarDefault
--storage-access-modeFLUREE_STORAGE_ACCESS_MODEshared

Options:

  • shared: Direct storage access (requires --storage-path or --connection-config)
  • proxy: Proxy reads through transaction server

For proxy mode:

FlagEnv Var
--storage-proxy-tokenFLUREE_STORAGE_PROXY_TOKEN
--storage-proxy-token-fileFLUREE_STORAGE_PROXY_TOKEN_FILE

Storage Proxy Configuration (Transaction Server)

Storage proxy provides replication-scoped access to raw storage for peer servers and CLI replication commands (fetch/pull/push). Tokens must carry fluree.storage.* claims — query-scoped tokens (fluree.ledger.read/write.*) are not sufficient. See Replication vs Query Access above.

Enable storage proxy endpoints for peers without direct storage access:

FlagEnv VarDefault
--storage-proxy-enabledFLUREE_STORAGE_PROXY_ENABLEDfalse
--storage-proxy-trusted-issuerFLUREE_STORAGE_PROXY_TRUSTED_ISSUERSNone
--storage-proxy-default-identityFLUREE_STORAGE_PROXY_DEFAULT_IDENTITYNone
--storage-proxy-default-policy-classFLUREE_STORAGE_PROXY_DEFAULT_POLICY_CLASSNone
--storage-proxy-debug-headersFLUREE_STORAGE_PROXY_DEBUG_HEADERSfalse
# Ed25519 trust (did:key):
fluree-server \
  --storage-proxy-enabled \
  --storage-proxy-trusted-issuer did:key:z6Mk...

# OIDC/JWKS trust (same --jwks-issuer flag used by other endpoints):
fluree-server \
  --storage-proxy-enabled \
  --jwks-issuer "https://solo.example.com=https://solo.example.com/.well-known/jwks.json"

JWKS support: When --jwks-issuer is configured, storage proxy endpoints accept RS256 OIDC tokens in addition to Ed25519 JWS tokens. The --jwks-issuer flag is shared with data, admin, and events endpoints — a single flag enables OIDC across all endpoint groups.

Complete Configuration Examples

Development (Memory Storage)

fluree-server \
  --log-level debug

Single Server (File Storage)

fluree-server \
  --storage-path /var/lib/fluree \
  --indexing-enabled \
  --log-level info

Production with Admin Auth

fluree-server \
  --storage-path /var/lib/fluree \
  --indexing-enabled \
  --admin-auth-mode required \
  --admin-auth-trusted-issuer did:key:z6Mk... \
  --log-level info

Transaction Server with Events Auth

fluree-server \
  --storage-path /var/lib/fluree \
  --events-auth-mode required \
  --events-auth-trusted-issuer did:key:z6Mk... \
  --storage-proxy-enabled \
  --admin-auth-mode required

Production with OIDC (All Endpoints)

fluree-server \
  --storage-path /var/lib/fluree \
  --indexing-enabled \
  --jwks-issuer "https://auth.example.com=https://auth.example.com/.well-known/jwks.json" \
  --data-auth-mode required \
  --events-auth-mode required \
  --admin-auth-mode required \
  --storage-proxy-enabled

Query Peer (Shared Storage)

fluree-server \
  --server-role peer \
  --tx-server-url http://tx.internal:8090 \
  --storage-path /var/lib/fluree \
  --peer-subscribe-all \
  --peer-events-token @/etc/fluree/peer-token.jwt

Query Peer (Proxy Storage)

fluree-server \
  --server-role peer \
  --tx-server-url http://tx.internal:8090 \
  --storage-access-mode proxy \
  --storage-proxy-token @/etc/fluree/storage-proxy.jwt \
  --peer-subscribe-all \
  --peer-events-token @/etc/fluree/peer-token.jwt

S3 + DynamoDB (Connection Config)

fluree server run \
  --connection-config /etc/fluree/connection.jsonld \
  --indexing-enabled \
  --reindex-min-bytes 100000 \
  --reindex-max-bytes 5000000 \
  --cache-max-mb 4096

With a config file:

[server]
connection_config = "/etc/fluree/connection.jsonld"
cache_max_mb = 4096
disk_cache_max_mb = 20480

[server.indexing]
enabled = true
reindex_min_bytes = 100000
reindex_max_bytes = 5000000

[server.auth.data]
mode = "required"
trusted_issuers = ["did:key:z6Mk..."]

S3 Peer (Shared Storage via Connection Config)

fluree server run \
  --server-role peer \
  --tx-server-url http://tx.internal:8090 \
  --connection-config /etc/fluree/connection.jsonld \
  --peer-subscribe-all \
  --peer-events-token @/etc/fluree/peer-token.jwt

Environment Variables Reference

VariableDescriptionDefault
FLUREE_HOMEGlobal Fluree directory (unified config + data)Platform dirs (see Global Directory Layout)
FLUREE_CONFIGConfig file path.fluree/config.{toml,jsonld} (auto-discovered)
FLUREE_PROFILEConfiguration profile nameNone
FLUREE_LISTEN_ADDRServer address:port0.0.0.0:8090
FLUREE_STORAGE_PATHFile storage path.fluree/storage
FLUREE_CONNECTION_CONFIGJSON-LD connection config file pathNone
FLUREE_CORS_ENABLEDEnable CORStrue
FLUREE_INDEXING_ENABLEDEnable background indexingtrue
FLUREE_REINDEX_MIN_BYTESSoft reindex threshold (bytes)100000
FLUREE_REINDEX_MAX_BYTESHard reindex threshold (bytes)20% of system RAM (256 MB fallback)
FLUREE_CACHE_MAX_MBGlobal in-memory cache budget (MB)Tiered by RAM: <4GB: 30%, 4-8GB: 40%, >=8GB: 35%
FLUREE_DISK_CACHE_MAX_MBGlobal on-disk cache budget (MB), shared across object storage + IcebergAuto-detect from free disk; 0 disables
FLUREE_DISK_CACHE_BUDGET_BYTESOn-disk cache budget (bytes); overrides FLUREE_DISK_CACHE_MAX_MBAuto-detect from free disk; 0 disables
FLUREE_BODY_LIMITMax request body bytes52428800
FLUREE_QUERY_TIMEOUT_MSMax query execution time in milliseconds (0 disables)900000
FLUREE_QUERY_MIN_T_TIMEOUT_MSMax read-after-write min-t wait in milliseconds5000
FLUREE_STREAM_HEARTBEAT_MSStreaming-query heartbeat interval in ms (0 disables)15000
FLUREE_QUERY_REFRESH_ENABLEDEnable TTL-gated nameservice refresh before current-head queriesfalse
FLUREE_QUERY_REFRESH_TTL_MSQuery-time refresh interval per ledger per process (0 checks every request)1000
FLUREE_LOG_LEVELLog levelinfo
FLUREE_SERVER_ROLEServer roletransaction
FLUREE_TX_SERVER_URLTransaction server URLNone
FLUREE_EVENTS_AUTH_MODEEvents auth modenone
FLUREE_EVENTS_AUTH_TRUSTED_ISSUERSEvents trusted issuersNone
FLUREE_DATA_AUTH_MODEData API auth modenone
FLUREE_DATA_AUTH_AUDIENCEData API expected audienceNone
FLUREE_DATA_AUTH_TRUSTED_ISSUERSData API trusted issuersNone
FLUREE_DATA_AUTH_DEFAULT_POLICY_CLASSData API default policy classNone
FLUREE_ADMIN_AUTH_MODEAdmin auth modenone
FLUREE_ADMIN_AUTH_TRUSTED_ISSUERSAdmin trusted issuersNone
FLUREE_MCP_ENABLEDEnable MCP endpointfalse
FLUREE_MCP_AUTH_TRUSTED_ISSUERSMCP trusted issuersNone
FLUREE_MCP_AGENT_JSON_MAX_BYTESMCP sparql_query Agent JSON byte budget32768
FLUREE_MCP_QUERY_TIMEOUT_MSMCP sparql_query execution timeout300000
FLUREE_STORAGE_ACCESS_MODEPeer storage modeshared
FLUREE_STORAGE_PROXY_ENABLEDEnable storage proxyfalse

Command-Line Reference

fluree-server --help

Best Practices

1. Keep Secrets Out of Config Files

Tokens and credentials should not be stored as plaintext in config files (which may be committed to version control or readable by other processes). Three options, in order of preference:

Environment variables (recommended for production):

export FLUREE_PEER_EVENTS_TOKEN=$(cat /etc/fluree/token.jwt)
export FLUREE_STORAGE_PROXY_TOKEN=$(cat /etc/fluree/proxy-token.jwt)

@filepath references in config files or CLI flags (reads the file at startup):

[server.peer]
events_token = "@/etc/fluree/peer-token.jwt"
storage_proxy_token = "@/etc/fluree/proxy-token.jwt"
--peer-events-token @/etc/fluree/token.jwt

Direct values (development only): If a secret-bearing field contains a literal token in the config file, the server logs a warning at startup recommending @filepath or env vars.

The following config file fields support @filepath resolution:

Config file keyEnv var alternative
peer.events_tokenFLUREE_PEER_EVENTS_TOKEN
peer.storage_proxy_tokenFLUREE_STORAGE_PROXY_TOKEN

2. Enable Admin Auth in Production

Always protect admin endpoints in production:

fluree-server \
  --admin-auth-mode required \
  --admin-auth-trusted-issuer did:key:z6Mk...

3. Use File Storage for Persistence

Memory storage is lost on restart:

# Development only
fluree-server

# Production
fluree-server --storage-path /var/lib/fluree

4. Monitor Logs

Use structured logging for production:

fluree-server --log-level info 2>&1 | jq .

Remote Connections

Remote connections enable SPARQL SERVICE federation against other Fluree instances. A remote connection maps a name to a server URL and bearer token. Once registered, queries can reference any ledger on that server using SERVICE <fluree:remote:<name>/<ledger>> { ... }.

Rust API

Register remote connections on the FlureeBuilder:

#![allow(unused)]
fn main() {
let fluree = FlureeBuilder::file("./data")
    .remote_connection("acme", "https://acme-fluree.example.com", Some(token))
    .remote_connection("partner", "https://partner.example.com", None)
    .build()?;
}

Each call registers a named connection. The name is used in SPARQL queries:

SERVICE <fluree:remote:acme/customers:main> { ?s ?p ?o }
SERVICE <fluree:remote:partner/inventory:main> { ?item ex:sku ?sku }

Connection Parameters

ParameterDescription
nameAlias used in fluree:remote:<name>/... URIs
base_urlServer URL (e.g., https://acme-fluree.example.com). The query path /v1/fluree/query/{ledger} is appended automatically.
tokenOptional bearer token for authentication. Sent as Authorization: Bearer <token> on every request.

The default per-request timeout is 30 seconds. Requests that exceed this produce a query error (or empty results with SERVICE SILENT).

Security

Bearer tokens are stored in memory on the Fluree instance. They are never serialized to storage, included in nameservice records, or exposed through info/admin endpoints. If the token needs rotation, rebuild the Fluree instance with an updated token, or use set_remote_service() to inject a custom executor with token refresh logic.

Feature Flag

The HTTP transport for remote SERVICE requires the search-remote-client Cargo feature (which enables reqwest). Without this feature, remote connections can be registered but queries against them will fail at runtime. The feature is enabled by default in the server binary.

See SPARQL: Remote Fluree Federation for query syntax and examples.

Memory Allocator (mimalloc)

fluree-db-server and fluree-db-cli expose an opt-in mimalloc Cargo feature that swaps the global allocator to mimalloc. It is off by default — enable it in your release build:

cargo build --release -p fluree-db-server --features mimalloc

When it helps: allocation-heavy, multicore query paths — most notably R2RML/Iceberg graph-source scans, which materialize many RDF terms in parallel. On those workloads the system allocator’s lock contention caps the parallel speedup; mimalloc roughly doubles the realized gain (e.g. TPC-H Q1 over a 6M-row Iceberg table: ~62s with the system allocator vs ~31s with mimalloc).

Tradeoffs (operational, not semantic):

  • Changes the allocator for every allocation in the process (HTTP, query, import, caches, AWS/Iceberg clients, tracing buffers).
  • Usually better multicore throughput and lower fragmentation, but may retain more RSS due to per-thread heaps — relevant for large scans that already peak high. Track RSS, p95 query latency, and concurrent-query behavior during a soak before making it the default.
  • New native dependency in the binary; verify it builds for your target (e.g. musl/cross builds) and keep a system-allocator fallback build.

Pair it with bounded query parallelism so a single large scan cannot monopolize cores and allocator pressure under concurrent load.

Iceberg / R2RML Graph-Source Tuning

Queries against an Iceberg-backed R2RML graph source are tuned by a set of environment-only knobs. All are optional; the defaults are chosen for correct, reasonable behavior out of the box.

VariableDefaultPurpose
FLUREE_ICEBERG_LOADTABLE_CACHEonMaster switch for all REST catalog caching. Set to 0/false/off to build a fresh catalog client and reload the table on every scan (restores a per-scan OAuth exchange + loadTable round-trip). Disables the client/OAuth reuse, the cross-query loadTable cache, and the per-query snapshot pin.
FLUREE_ICEBERG_LOADTABLE_TTL_SECS60TTL (seconds) for the cross-query loadTable-response cache. A REST loadTable GET against a catalog such as Snowflake Horizon costs ~1.3–3 s, so caching it lets a burst of queries against the same table skip the round-trip. The TTL bounds how stale a snapshot a new query may observe; 0 disables the cross-query layer (leaving only the per-query pin). Every cache read is additionally gated on vended-credential expiry (30 s buffer), so a long TTL never hands out about-to-expire credentials.
FLUREE_ICEBERG_REST_CLIENT_TTL_SECS900TTL (seconds) for the process-wide REST catalog-client cache (the reused OAuth token + HTTPS pool). The cache is keyed by a fingerprint of the raw config JSON, which does not change when a secret referenced by env var / secret store is rotated; this TTL bounds how long a rotated secret stays stale before the client is rebuilt and re-authenticated. 0 rebuilds the client every query (restoring a per-query OAuth exchange).
FLUREE_R2RML_SCAN_CACHEonToggles the correlated-join inner-scan cache, which reuses a materialized inner (dimension) table across a join’s child batches instead of re-scanning it per batch. Set to 0/false/off to restore per-child-batch re-scans.
FLUREE_R2RML_LIMIT_PUSHDOWNonToggles pushing a query’s LIMIT into the R2RML scan as a row budget, so a scan stops after enough output rows instead of draining the table. Set to 0/false/off to always scan fully.
FLUREE_ICEBERG_PREDICATE_PUSHDOWNonToggles pushing simple date/int/bool/string FILTER comparisons into the Iceberg reader for row-group pruning (skip groups whose min/max rule out the predicate) and exact row filtering (drop non-matching rows during decode — e.g. = "Acme" on a name/code column). The in-engine FILTER remains the authority, so this only ever affects performance. Set to 0/false/off to disable both.
FLUREE_R2RML_FILTER_CONSUMPTIONonToggles folding a fully scan-local FILTER into the single R2RML scan that produces its variables, dropping the standalone filter operator so a LIMIT budget can reach the scan (a FILTER + LIMIT fact query then stops early). The scan re-applies the filter with the same evaluator, so results are unchanged. Set to 0/false/off to keep the filter downstream (no early termination).
FLUREE_R2RML_MATERIALIZE_WINDOW_ROWS524288Target table rows materialized into RDF-term bindings per parallel window. Bounds resident memory during a scan (materialization explodes the compact columnar form into fat binding rows). Also caps the size of an inner scan eligible for the inner-scan cache above.
FLUREE_ICEBERG_SCAN_CONCURRENCYmin(cores, files, 8)Number of data files read concurrently within one scan. Raise it for high-latency remote object stores (it is not capped, but is bounded by the number of files in the scan).

Caching model. Catalog access is cached at two scopes. Within a single query, the first scan of a table pins its metadata_location, so every scan in that query reads one consistent Iceberg snapshot even if the table commits mid-query. Across queries, a process-wide cache reuses the REST client (so its OAuth token — valid ~1 h — and HTTPS connection pool survive) and the loadTable response (bounded by FLUREE_ICEBERG_LOADTABLE_TTL_SECS). On a warm server this means the second and later queries against a table typically skip both the OAuth exchange and the loadTable GET entirely. The cross-query cache always records the catalog’s current state, never a query’s pinned snapshot, so pin preservation cannot leak a stale location to other queries. The client cache is keyed by a fingerprint of the raw config JSON: editing the config (including an inline secret) rebuilds the client immediately, but a secret referenced by env var or secret store is invisible to the fingerprint, so a rotation of that secret is picked up only when the client cache entry expires (FLUREE_ICEBERG_REST_CLIENT_TTL_SECS, default 15 min) — until then the reused client keeps presenting the old credential.

Freshness vs. latency. The only knob with a data-freshness tradeoff is FLUREE_ICEBERG_LOADTABLE_TTL_SECS: a new query may read a snapshot up to that many seconds old. This is well within typical warehouse ETL latency; lower it (or set 0) if you need each query to always resolve the newest snapshot, at the cost of paying the loadTable GET per query.