Sign inSign up

fluree/server

By fluree

•Updated 6 days ago

Fluree knowledge graph db. SPARQL, Cypher, policy, reasoning, time travel, raw speed.

Image
Databases & storage
4

10K+

fluree/server repository overview

⁠Running Fluree with Docker

For full documentation, visit labs.flur.ee⁠.

The official image (fluree/server) ships the fluree binary on a slim Debian base. This guide covers what's inside the image, how to configure it (env vars, mounted config files, CLI flags), and worked recipes for the common production patterns.

⁠What's in the Image

AspectValue
Basedebian:trixie-slim
Entrypoint/usr/local/bin/fluree-entrypoint.sh
Default commandfluree server run
WORKDIR/var/lib/fluree
VOLUME/var/lib/fluree
Exposed port8090
Runtime userfluree (UID 1000, GID 1000)
HealthcheckGET /health every 30s
Default log filterRUST_LOG=info

Entrypoint behavior: on first start, if /var/lib/fluree/.fluree/ does not exist, the entrypoint runs fluree init to create a default .fluree/config.toml and .fluree/storage/ directory. Subsequent starts skip init. Any arguments passed to docker run after the image name are forwarded to fluree server run, so you can append CLI flags (e.g. --log-level debug) directly.

⁠Quick Start

docker run --rm -p 8090:8090 fluree/server:latest

Verify:

curl http://localhost:8090/health

Data lives inside the container's writable layer here — fine for trying things out, lost when the container is removed. For anything beyond a smoke test, mount a volume.

⁠Persisting Data

The image declares VOLUME /var/lib/fluree. Mount a host directory or named volume there:

# Named volume (recommended)
docker run -d --name fluree \
  -p 8090:8090 \
  -v fluree-data:/var/lib/fluree \
  fluree/server:latest

# Host bind mount — make sure the directory is writable by UID 1000
mkdir -p ./fluree-data && sudo chown 1000:1000 ./fluree-data
docker run -d --name fluree \
  -p 8090:8090 \
  -v "$PWD/fluree-data:/var/lib/fluree" \
  fluree/server:latest

The volume holds both .fluree/config.toml (config) and .fluree/storage/ (ledger data) by default.

⁠Three Ways to Configure

Fluree resolves configuration with this precedence (highest wins):

  1. CLI flags appended after the image name
  2. Environment variables (FLUREE_*) set with -e or environment:
  3. Profile overrides ([profiles.<name>.server]) when you pass --profile
  4. Config file at .fluree/config.toml or .fluree/config.jsonld
  5. Built-in defaults

You can use any one of these — or, more typically, layer them: bake a base config file into a volume, then tweak per-environment with env vars or compose overrides.

Heads up — log level: The Dockerfile sets ENV RUST_LOG=info. The console log filter uses RUST_LOG if it is non-empty and only falls back to FLUREE_LOG_LEVEL when RUST_LOG is unset. Inside this image you must override RUST_LOG to change console verbosity:

docker run -e RUST_LOG=debug fluree/server:latest
⁠1. Environment Variables Only

Every CLI flag has a FLUREE_* env var equivalent (see Configuration⁠). For simple deployments this is the lowest-friction path:

docker run -d --name fluree \
  -p 8090:8090 \
  -v fluree-data:/var/lib/fluree \
  -e FLUREE_LISTEN_ADDR=0.0.0.0:8090 \
  -e FLUREE_STORAGE_PATH=/var/lib/fluree/.fluree/storage \
  -e FLUREE_INDEXING_ENABLED=true \
  -e FLUREE_REINDEX_MIN_BYTES=1000000 \
  -e FLUREE_REINDEX_MAX_BYTES=10000000 \
  -e FLUREE_CACHE_MAX_MB=2048 \
  -e RUST_LOG=info \
  fluree/server:latest
⁠2. Mounted Config File (JSON-LD or TOML)

Author a config file on the host, then mount it at /var/lib/fluree/.fluree/config.jsonld (or .toml). The server walks up from WORKDIR=/var/lib/fluree and picks it up automatically.

./fluree-config/config.jsonld:

{
  "@context": { "@vocab": "https://ns.flur.ee/config#" },
  "server": {
    "listen_addr": "0.0.0.0:8090",
    "storage_path": "/var/lib/fluree/.fluree/storage",
    "log_level": "info",
    "cache_max_mb": 2048,
    "indexing": {
      "enabled": true,
      "reindex_min_bytes": 1000000,
      "reindex_max_bytes": 10000000
    }
  },
  "profiles": {
    "prod": {
      "server": {
        "log_level": "warn",
        "cache_max_mb": 8192
      }
    }
  }
}
docker run -d --name fluree \
  -p 8090:8090 \
  -v fluree-data:/var/lib/fluree \
  -v "$PWD/fluree-config/config.jsonld:/var/lib/fluree/.fluree/config.jsonld:ro" \
  fluree/server:latest --profile prod

If both config.toml and config.jsonld exist in the same directory, TOML wins and the server logs a warning. Pick one format.

The TOML equivalent (./fluree-config/config.toml):

[server]
listen_addr = "0.0.0.0:8090"
storage_path = "/var/lib/fluree/.fluree/storage"
log_level = "info"
cache_max_mb = 2048

[server.indexing]
enabled = true
reindex_min_bytes = 1000000
reindex_max_bytes = 10000000

[profiles.prod.server]
log_level = "warn"
cache_max_mb = 8192

You can also stash the config outside WORKDIR and point at it explicitly:

docker run -d --name fluree \
  -p 8090:8090 \
  -v fluree-data:/var/lib/fluree \
  -v "$PWD/fluree-config:/etc/fluree:ro" \
  fluree/server:latest --config /etc/fluree/config.jsonld
⁠3. Layered: File + Env Var Overrides

The common production shape: bake the base config into the image or volume, then let the orchestrator override per-environment with FLUREE_* env vars. Env vars beat the file — no file edit needed to bump cache size in staging vs. prod.

docker run -d --name fluree \
  -p 8090:8090 \
  -v fluree-data:/var/lib/fluree \
  -v "$PWD/fluree-config/config.jsonld:/var/lib/fluree/.fluree/config.jsonld:ro" \
  -e FLUREE_CACHE_MAX_MB=4096 \
  -e RUST_LOG=warn \
  fluree/server:latest

⁠Common Configuration Recipes

⁠Tuning the LRU Cache

cache_max_mb is the global budget for the in-memory index/flake cache. The default is tiered by effective system memory: <4GB uses 30%, 4-8GB uses 40%, and >=8GB uses 35%. On a container with a hard memory limit, the auto-tier clamps to the cgroup limit when available; set this explicitly if you need a tighter cache budget.

# docker-compose.yml fragment
services:
  fluree:
    image: fluree/server:latest
    mem_limit: 6g
    environment:
      FLUREE_CACHE_MAX_MB: 3072    # ~50% of the cgroup limit

Or in JSON-LD:

{
  "@context": { "@vocab": "https://ns.flur.ee/config#" },
  "server": { "cache_max_mb": 3072 }
}
⁠Background Indexing

Indexing is off by default. Enable it for production write workloads — without it, every commit writes to novelty and queries get slower as novelty grows.

SettingMeaning
indexing.enabledTurn the background indexer on
reindex_min_bytesSoft threshold — novelty above this triggers a background reindex
reindex_max_bytesHard threshold — commits block above this until reindexing catches up

Tune min/max based on commit volume. Defaults (100 KB / 1 MB) are conservative; busy ledgers should raise both:

[server.indexing]
enabled = true
reindex_min_bytes = 5000000     # 5 MB — start indexing in the background
reindex_max_bytes = 50000000    # 50 MB — block commits at this point
docker run -d \
  -e FLUREE_INDEXING_ENABLED=true \
  -e FLUREE_REINDEX_MIN_BYTES=5000000 \
  -e FLUREE_REINDEX_MAX_BYTES=50000000 \
  fluree/server:latest
⁠CORS and Request Body Size
[server]
cors_enabled = true
body_limit = 104857600    # 100 MB — raise for bulk imports
⁠Authentication (Production)

Require a Bearer token on data and admin endpoints. The trusted issuer is the did:key of your token signer.

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

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

For OIDC/JWKS (e.g. an external IdP), set --jwks-issuer or FLUREE_JWKS_ISSUERS:

docker run -d \
  -e FLUREE_DATA_AUTH_MODE=required \
  -e FLUREE_JWKS_ISSUERS="https://auth.example.com=https://auth.example.com/.well-known/jwks.json" \
  fluree/server:latest

See Configuration → Authentication⁠ for the full matrix.

⁠S3 + DynamoDB (Distributed Storage)

For multi-node or cloud deployments, point the server at a JSON-LD connection config describing your storage and nameservice. AWS credentials come from the standard SDK chain (env vars, IAM role, etc.) — they are not part of the connection config.

./fluree-config/connection.jsonld:

{
  "@context": {
    "@base": "https://ns.flur.ee/config/connection/",
    "@vocab": "https://ns.flur.ee/system#"
  },
  "@graph": [
    { "@id": "commitStorage", "@type": "Storage",
      "s3Bucket": "fluree-prod-commits", "s3Prefix": "data/" },
    { "@id": "indexStorage", "@type": "Storage",
      "s3Bucket": "fluree-prod-indexes" },
    { "@id": "publisher", "@type": "Publisher",
      "dynamodbTable": "fluree-nameservice", "dynamodbRegion": "us-east-1" },
    { "@id": "conn", "@type": "Connection",
      "commitStorage": { "@id": "commitStorage" },
      "indexStorage":  { "@id": "indexStorage" },
      "primaryPublisher": { "@id": "publisher" } }
  ]
}
docker run -d --name fluree \
  -p 8090:8090 \
  -v "$PWD/fluree-config:/etc/fluree:ro" \
  -e AWS_REGION=us-east-1 \
  -e AWS_ACCESS_KEY_ID=... \
  -e AWS_SECRET_ACCESS_KEY=... \
  -e FLUREE_CONNECTION_CONFIG=/etc/fluree/connection.jsonld \
  -e FLUREE_INDEXING_ENABLED=true \
  fluree/server:latest

--connection-config and --storage-path are mutually exclusive. See Configuration → Connection Configuration⁠ and the DynamoDB guide⁠ for backend-specific setup.

⁠Search Service (fluree-search-httpd)

Run a dedicated BM25 / vector search service alongside the main server when search traffic is heavy enough that you want it isolated from the transactional path. The service is a separate binary with its own listen port — it is not mounted under the main server's api_base_url. It needs read access to the same storage and nameservice paths the main server writes to.

docker run -d --name fluree-search \
  -p 9090:9090 \
  -v fluree-data:/var/lib/fluree \
  -e FLUREE_STORAGE_ROOT=/var/lib/fluree/storage \
  -e FLUREE_NAMESERVICE_PATH=/var/lib/fluree/ns \
  fluree/search-httpd:latest
Env varDefaultPurpose
FLUREE_STORAGE_ROOT(required)Storage path (file:// optional)
FLUREE_NAMESERVICE_PATH(required)Nameservice path
FLUREE_SEARCH_LISTEN0.0.0.0:9090Listen address
FLUREE_SEARCH_CACHE_MAX_ENTRIES100Max cached indexes
FLUREE_SEARCH_CACHE_TTL_SECS300Cache TTL
FLUREE_SEARCH_MAX_LIMIT1000Max results per query
FLUREE_SEARCH_DEFAULT_TIMEOUT_MS30000Default request timeout
FLUREE_SEARCH_MAX_TIMEOUT_MS300000Maximum allowed timeout

Prerequisites. The service only serves queries against indexes that already exist on the shared volume. BM25 / vector graph-source indexes are created via the Rust API today (Bm25CreateConfig + create_full_text_index, or VectorCreateConfig + create_vector_index). The @fulltext datatype and the f:fullTextDefaults config-graph paths are managed entirely through the main server's HTTP API and don't require this dedicated service.

Compose example with both services sharing a volume:

services:
  fluree:
    image: fluree/server:latest
    ports:
      - "8090:8090"
    volumes:
      - fluree-data:/var/lib/fluree
    environment:
      RUST_LOG: info
      FLUREE_INDEXING_ENABLED: "true"

  fluree-search:
    image: fluree/search-httpd:latest
    depends_on:
      - fluree
    ports:
      - "9090:9090"
    volumes:
      - fluree-data:/var/lib/fluree:ro     # read-only is sufficient
    environment:
      RUST_LOG: info
      FLUREE_STORAGE_ROOT: /var/lib/fluree/storage
      FLUREE_NAMESERVICE_PATH: /var/lib/fluree/ns

volumes:
  fluree-data:

Clients send search requests to POST http://fluree-search:9090/v1/search. See BM25 → Remote Search Service⁠ for the request/response protocol.

⁠Query Peer

Run as a read-only peer that subscribes to a transaction server's event stream:

docker run -d --name fluree-peer \
  -p 8090:8090 \
  -v fluree-peer-data:/var/lib/fluree \
  -e FLUREE_SERVER_ROLE=peer \
  -e FLUREE_TX_SERVER_URL=http://tx.internal:8090 \
  fluree/server:latest --peer-subscribe-all

See Query peers and replication⁠ for the proxy-mode and auth options.

⁠Docker Compose: Full Example

A production-leaning single-node setup with a mounted JSON-LD config, env-var overrides, named data volume, and resource limits:

services:
  fluree:
    image: fluree/server:latest
    container_name: fluree
    restart: unless-stopped
    ports:
      - "8090:8090"
    volumes:
      - fluree-data:/var/lib/fluree
      - ./fluree-config/config.jsonld:/var/lib/fluree/.fluree/config.jsonld:ro
    environment:
      RUST_LOG: info
      FLUREE_CACHE_MAX_MB: 4096
      FLUREE_INDEXING_ENABLED: "true"
      FLUREE_REINDEX_MIN_BYTES: "5000000"
      FLUREE_REINDEX_MAX_BYTES: "50000000"
      # Auth — point at your trusted did:key signer
      FLUREE_DATA_AUTH_MODE: required
      FLUREE_DATA_AUTH_TRUSTED_ISSUERS: did:key:z6Mk...
      FLUREE_ADMIN_AUTH_MODE: required
      FLUREE_ADMIN_AUTH_TRUSTED_ISSUERS: did:key:z6Mk...
    mem_limit: 8g
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8090/health"]
      interval: 30s
      timeout: 3s
      start_period: 15s
      retries: 3
    command: ["--profile", "prod"]

volumes:
  fluree-data:
docker compose up -d
docker compose logs -f fluree

⁠Troubleshooting

Container restarts after fluree init. First-run init only runs when /var/lib/fluree/.fluree/ is missing. If the volume is owned by a non-1000 UID, init fails. Fix with sudo chown -R 1000:1000 ./fluree-data on the host.

Mounted config file is ignored. Confirm the mount path and the file extension. The server only auto-discovers .fluree/config.toml or .fluree/config.jsonld under the working directory. Anything else needs --config <path> (or FLUREE_CONFIG=<path>). If both formats are present in the same directory, TOML wins — check the startup logs for the warning.

Setting FLUREE_LOG_LEVEL doesn't change console output. The image's ENV RUST_LOG=info shadows it. Override with -e RUST_LOG=debug instead.

cache_max_mb auto-default is too large under a memory limit. Current builds clamp the auto-tier to the cgroup limit when available. If logs still show an oversized cache, check for an older image/binary or an explicit FLUREE_CACHE_MAX_MB / cache_max_mb override.

Health check failing. curl http://localhost:8090/health from your host. If the server is up but the healthcheck fails, the listen address is probably bound to 127.0.0.1 inside the container — set FLUREE_LISTEN_ADDR=0.0.0.0:8090.

Tag summary

Content type

Image

Digest

sha256:14da004ab…

Size

80.7 MB

Last updated

6 days ago

docker pull fluree/server