The CLI exposes the edge backend through a global --edge flag plus a dedicated qql edge subcommand group and qql config edge configuration subcommand. Edge must be compiled in first — see getting started.
Global --edge flag
Section titled “Global --edge flag”--edge is a global flag, not a subcommand. Add it to any execution-facing command to route it to the in-process backend instead of QDRANT_URL:
| Command | Remote form | Edge form |
|---|---|---|
| Run one statement or script | qql run "…" | qql --edge run "…" |
Run a .qql file | qql run file.qql | qql --edge run file.qql |
| Interactive REPL | qql repl | qql --edge repl |
| Export a collection | qql dump docs out.qql | qql --edge dump docs out.qql |
| Health check | qql doctor | qql --edge doctor |
| Merge segments / build indexes | — | qql edge optimize docs |
| Seed from a remote shard snapshot | — | qql edge bootstrap docs --from http://host:6333 |
qql --edge run "SHOW COLLECTIONS" qql --edge run "QUERY 'hello' FROM docs USING dense LIMIT 5" qql --edge run first-local.qql qql --edge dump docs backup.qql qql --edge repl qql edge optimize docs qql edge bootstrap docs --from http://localhost:6333qql explain needs no backend and therefore no --edge flag — planning is identical for remote and edge backends.
qql edge optimize
Section titled “qql edge optimize”qdrant-edge has no background optimizer. Segments are merged and HNSW / sparse indexes are built only when optimization is explicitly requested, so a bulk-loaded collection can serve search from unindexed (brute-force) segments indefinitely. qql edge optimize <collection> runs the engine's blocking optimizer loop until no plan remains and prints the counts before and after:
Optimized 'docs': 5 → 2 segments, 300 points, indexed 0 → 300or, when nothing needed rebuilding:
'collection' is already optimal: 2 segments, 300 points, indexed 300The command builds the edge backend directly (no embedding model is loaded or downloaded). --json emits operation: "edge-optimize", optimized, and before/after {points, indexed, segments} objects. When indexed_vectors_count < points_count after optimizing, the result carries status: "warn" / indexing_lag: true and the message reports the exact lag instead of claiming success — the command still exits zero.
Run it after bulk writes, or when qql --edge doctor / qql check --edge reports indexing lag (see below). Small collections can legitimately stay unindexed: the indexing optimizer only builds HNSW for segments above the collection's indexing_threshold (10 MB by default offline, configurable with WITH OPTIMIZERS (indexing_threshold = …)).
qql edge bootstrap
Section titled “qql edge bootstrap”qql edge bootstrap <collection> --from <remote-url> seeds a local edge collection from a remote Qdrant shard snapshot — the documented "offload indexing" flow. The snapshot is streamed from GET /collections/{c}/shards/{id}/snapshot, unpacked with the engine's snapshot API into a staging directory, verified by loading it, and only then moved into the data directory. It carries the source collection's configuration, built HNSW indexes and quantized data, so the device does not re-index anything.
Flags:
| Flag | Purpose |
|---|---|
--from <url> | Remote Qdrant base URL (defaults to --url / QDRANT_URL) |
--api-key <key> | Remote API key (defaults to QDRANT_API_KEY) |
--shard-id <n> | Remote shard to stream; required for multi-shard collections |
--force | Replace an existing local collection (verified before the swap) |
--json | Machine-readable result |
Shard discovery: for a single-shard collection the shard id is found automatically via GET /collections/{c}/cluster. A collection with more than one shard fails closed with QQL-SNAPSHOT-SHARD unless --shard-id is given — a qdrant-edge collection is exactly one shard, so you seed one shard per device (this pairs with the per-device shard pattern for multi-tenant sources).
An existing local collection is never overwritten without --force, and the replacement only happens after the snapshot downloads and verifies. Failures leave the previous collection untouched, and with --json they emit a structured {ok: false, operation: "edge-bootstrap", collection, message} on stdout before exiting non-zero. The same command is exposed as an #[ignore] integration test: cargo test -p qql-cli --features edge --test edge_bootstrap -- --ignored.
qql config edge
Section titled “qql config edge”qql config edge writes <config-dir>/edge.json (default config directory is ~/.qql, file written with 0600 permissions). It is a patch update: only the flags you pass are written, and every key you did not set — including keys written by a newer version — is preserved. Against an existing file the merged configuration is validated as a whole, so a persisted --embedder http keeps its persisted --embed-url. After saving it prints:
Saved edge configuration to /home/you/.qql/edge.jsonUse it with: qql --edge exec "SHOW COLLECTIONS"Storage flags
Section titled “Storage flags”| Flag | Default | Purpose |
|---|---|---|
--data-dir <dir> | ~/.qql/edge-data | Directory for persistent qdrant-edge data |
--in-memory | off | Keep payloads in memory instead of persisting them to disk (sets on_disk_payload=false) |
--on-disk | on | Persist payloads to disk (flips a persisted --in-memory config back) |
--wal-segment-mb <n> | qdrant-edge 32 MiB | WAL segment capacity in MiB. qdrant-edge pre-allocates each segment to this size, which otherwise dominates the on-disk footprint of small embedded shards. Seeds new shards (and shards without a persisted capacity); an already-persisted value wins on reopen. Must be greater than zero; also exposed by the Python/Node edge SDKs (qdrant-edge 0.8's own Python binding cannot set it) |
Embedding model flags (fastembed)
Section titled “Embedding model flags (fastembed)”| Flag | Default | Purpose |
|---|---|---|
--embedder fastembed|http | fastembed | Local ONNX inference or an OpenAI-compatible endpoint |
--model <name> | BGESmallENV15 (384-d) | Dense model name or alias |
--sparse-model <name> | local Qdrant-compatible BM25 | Offline sparse model (splade, bge-m3) |
--multi-model <name> | none | Offline multivector / ColBERT model (bge-m3) |
--image-model <name> | none | Offline CLIP vision model (clip-vision) |
--reranker-model <name> | none | Offline cross-encoder (bge-reranker-base) |
--cache-dir <dir> | fastembed default | Directory for downloaded models |
--show-download-progress | off | Show Hugging Face download progress bars (--no-show-download-progress flips a persisted config back) |
HTTP embedder flags
Section titled “HTTP embedder flags”These apply when --embedder http is selected. Storage and search stay local; only embeddings go over the network.
| Flag | Default | Purpose |
|---|---|---|
--embed-url <url> | none (required for http) | OpenAI-compatible embedding endpoint |
--embed-key <key> | empty | Bearer token (EMBED_KEY) |
--embed-model <name> | nomic-embed-text | Model name sent in the request body |
--embed-dim <n> | 768 | Expected output dimension |
--multi-embed-url / --multi-embed-key / --multi-embed-model / --multi-embed-dim | none | Optional multi/ColBERT endpoint (dim 0 skips the dimension check) |
--image-embed-url / --image-embed-key / --image-embed-model / --image-embed-dim | none | Optional image/CLIP endpoint (dim 0 uses the dense dimension) |
Validation rules: --embedder must be fastembed or http; http requires --embed-url; --embed-dim must be greater than zero; and --wal-segment-mb must be greater than zero when set.
Environment variables
Section titled “Environment variables”Every qql config edge setting can be overridden per invocation via environment. Edge-specific variables start with QQL_EDGE_; the HTTP embedder reuses the same variables as remote execution.
| Variable | Overrides | Notes |
|---|---|---|
QQL_EDGE_DATA_DIR | --data-dir | Data directory for qdrant-edge storage |
QQL_EDGE_EMBEDDER | --embedder | fastembed or http |
QQL_EDGE_MODEL | --model | Dense model name or alias |
QQL_EDGE_SPARSE_MODEL | --sparse-model | Offline sparse model |
QQL_EDGE_MULTI_MODEL | --multi-model | Offline multi/ColBERT model |
QQL_EDGE_IMAGE_MODEL | --image-model | Offline CLIP vision model |
QQL_EDGE_RERANKER_MODEL | --reranker-model | Also falls back to RERANK_MODEL |
QQL_EDGE_CACHE_DIR | --cache-dir | Model download cache directory |
QQL_EDGE_ON_DISK | --in-memory | true/false/1/0 — payloads on disk |
QQL_EDGE_WAL_SEGMENT_MB | --wal-segment-mb | WAL segment capacity in MiB (integer > 0); seeds shards without a persisted capacity |
EMBED_URL | --embed-url | HTTP embedding endpoint |
EMBED_KEY | --embed-key | Bearer token |
EMBED_MODEL | --embed-model | Model name |
EMBED_DIM | --embed-dim | Output dimension (integer) |
MULTI_EMBED_URL / MULTI_EMBED_KEY / MULTI_EMBED_MODEL / MULTI_EMBED_DIM | --multi-embed-* | Multi/ColBERT endpoint |
IMAGE_EMBED_URL / IMAGE_EMBED_KEY / IMAGE_EMBED_MODEL / IMAGE_EMBED_DIM | --image-embed-* | Image/CLIP endpoint |
QQL_EDGE_DATA_DIR=/tmp/ephemeral QQL_EDGE_MODEL=all-minilm-l6-v2 \qql --edge exec "QUERY 'hi' FROM docs USING dense LIMIT 3"Edge doctor output
Section titled “Edge doctor output”qql --edge doctor performs a real edge init (config load, model resolution, shard open) and then runs SHOW COLLECTIONS against it. It prints a summary of which embedding slots are configured and actionable hints:
Connected to the local edge backend (healthy)collections: edge backend (local, 2)collection 'docs': 300 points, 0 indexed — qdrant-edge only indexes during optimize; run `qql edge optimize docs` after bulk writes (segments below indexing_threshold stay brute-force)Hosts: dense=true multi=false image=false cross_rerank=false dense_model: BGESmallENV15 hint: ColBERT / AS MULTI / multivector RERANK needs multi_model or multi_embedding_* config hint: IMAGE / CLIP vision needs image_model or image_embedding_* config hint: CROSS RERANK needs reranker_model or rerank_endpoint / rerank_model hint: edge has no SHARD routing or shard-key DDL; ALTER COLLECTION covers HNSW/optimizers onlyqql check --edge "<statement>" adds the same per-collection readout as an edge-indexing stage (ok when indexed ≥ points, warn with the optimize nudge when it lags; warnings never fail the check).
The hints are guidance, not failures — the backend is healthy. If a configured model cannot be loaded (for example an unknown name), doctor reports the init error instead. The JSON form is available via qql --edge doctor --json, which emits backend, dense/multi/image/cross_rerank booleans, the resolved model names, the hints array, and an edge_indexing array of per-collection {collection, points_count, indexed_vectors_count, segments_count, nudge}.
Config file location and permissions
Section titled “Config file location and permissions”- File:
<config-dir>/edge.jsonwhere the config dir is~/.qql - Permissions:
0600on Unix (contains embedding API keys when using the HTTP embedder) - Default data dir:
<config-dir>/edge-data
For what happens inside the data directory, see persistence.