A Kibana-like log explorer UI for Quickwit. React frontend, choice of Go or Rust backend.
Browser
└── Vite dev server (port 5173) or built static files
└── /api/* → qwui backend (port 8080)
└── /quickwit/* → qwui backend (port 8080)
└── proxies to Quickwit (port 7280)
The backend handles auth, CSV export, log pattern detection (Drain algorithm), and proxies all Quickwit API calls.
qwui/
├── frontend/ # React + Vite app
│ ├── src/
│ └── public/
├── go-backend/ # Go backend
│ └── pattern/ # standalone Drain algorithm test tool
├── rust-backend/ # Rust backend (includes VRL support)
├── dev/ # Docker data generator (Python)
├── k6/ # Load tests
├── Dockerfile.go # Builds frontend + Go backend into one image
├── Dockerfile.rust # Builds frontend + Rust backend into one image
├── docker-compose.yml
└── Makefile
- Docker + Docker Compose
- Node.js 20+
- Go 1.24+ or Rust 1.75+
Start Quickwit and the data generator:
make devThen run whichever backend you want locally:
make run-go # cd go-backend && go run .
make run-rust # cd rust-backend && cargo runThen start the frontend:
cd frontend && npm install && npm run dev
# or: make run-frontendmake dev-go
make run-frontendmake dev-rust
make run-frontendmake down # stop all containers and wipe data volumesmake logs # all services
docker compose logs -f data-generator # generator only
docker compose logs -f qwui-go # Go backend
docker compose logs -f qwui-rust # Rust backend| Service | Profile | Description |
|---|---|---|
quickwit |
(always) | Quickwit search engine on port 7280 |
data-generator |
(always) | Continuously ingests sample logs into the demo index |
qwui-go |
go |
Go backend on port 8080 |
qwui-rust |
rust |
Rust backend on port 8080 |
qwui-go and qwui-rust both bind port 8080 — only one can run at a time.
The data generator runs continuously in two phases:
- Backfill — 50 000 events spread over the last 7 days, ingested on startup
- Stream — randomised bursts of events with current timestamps, runs forever
Three log types are generated so log pattern detection returns distinct clusters:
nginx— HTTP access logs (method, path, status, response time)app— Application logs (logins, orders, cache misses, slow queries)php— PHP errors and warnings with file/line
The index is named demo with timestamp as the timestamp field.
| Button | Description |
|---|---|
| Search | Runs the query. If the time range end is "now" (relative range), it is recomputed to the current time before executing. |
| ↑ / ↓ arrows | Navigate query history. Previous queries are stored in localStorage (up to 50 entries) and restored with their filters, time range, and VRL script. |
| Save | Saves the current query (search text, filters, time range, columns) into the qwui Quickwit index. |
| Load | Lists queries previously saved in the qwui index. |
| Share | Copies a URL to the clipboard encoding the full query state. Opening the URL restores the exact search. |
| ∿ Patterns | Analyses log patterns using the Drain algorithm. Samples events across time buckets, clusters them by similarity, and shows the top patterns with their percentage. A field selector lets you run the analysis on a specific field. |
| ƒ VRL | (Rust backend only) Opens a VRL (Vector Remap Language) script editor. The script is applied server-side to each result document before it is returned, allowing field extraction, renaming, or computed fields. |
| Export | Exports the current search results as a gzip-compressed CSV file, generated by the backend. |
Save and Load require a Quickwit index named qwui (configurable via VITE_SAVED_QUERIES_INDEX). Create it once:
curl -X POST http://localhost:7280/api/v1/indexes \
-H 'Content-Type: application/yaml' \
--data-binary @- <<'EOF'
version: 0.8
index_id: qwui
doc_mapping:
timestamp_field: timestamp
field_mappings:
- name: timestamp
type: datetime
fast: true
input_formats: [rfc3339]
output_format: rfc3339
- name: type
type: text
tokenizer: raw
- name: name
type: text
- name: query
type: json
stored: true
search_settings:
default_search_fields: [name, type]
EOFThe VRL button is only shown when the backend reports features.vrl: true in /api/auth/status. This is only the case with the Rust backend, which embeds the VRL runtime. The Go backend silently ignores any vrl field and always reports features.vrl: false.
Example VRL script:
# Parse a JSON string field into structured fields
. = parse_json!(.raw_message)
.level = upcase(.level)The execution time is shown next to the button after each search.
Select multiple indexes from the index dropdown. Indexes are searchable together only if they share the same timestamp field — indexes with a different timestamp field are shown as disabled with a tooltip explaining why.
When multiple indexes are selected:
- Histogram is disabled (field may not exist across all indexes)
sort_byis disabled for the same reason- Quickwit's native comma-separated index URL format is used internally (
/api/v1/index1,index2/search)
Click ∿ to cluster the visible logs using the Drain algorithm:
- The time range is split into 20 equal buckets, 500 events are sampled per bucket in parallel (10 000 events total), avoiding Quickwit's
start_offset > 10 000limit - Tokens that look like IDs (IPs, UUIDs, hex strings, numbers, tokens longer than 40 characters) are normalised to
<*> - Similar log lines are merged into a cluster; the similarity threshold is 0.4
- Results show the template with
<*>wildcards highlighted in orange, and the percentage of logs matching each pattern - Each pattern has a Show sample toggle to display an example log line
- A field selector lets you run the analysis on a specific field instead of the full document
Fields you frequently expand or add as columns are tracked per-index in localStorage under fieldPopularity. The top 10 most-used fields are shown in a Popular section at the top of the fields sidebar, above the full alphabetical list. A popular field is removed from the main list to avoid duplication. The section is hidden when empty.
Popularity is incremented when you click the + column button (not when expanding for top values).
Accessed via the ⚙ gear icon. Stored in localStorage under userPreferences.
| Setting | Description |
|---|---|
| Default tab | Whether documents open in Table or JSON view by default |
| Font size | Result table font size in pixels |
| JSON collapsed | Whether JSON view starts collapsed |
| Display data types | Show data type annotations in JSON view |
| Display object size | Show object/array size in JSON view |
Dark mode is toggled with the ☀/☾ button and stored separately in localStorage under darkMode.
Switch between Logs and Visualize using the buttons in the top bar.
In Visualize mode a GraphView panel replaces the results table. You can:
- Drag a numeric field from the sidebar onto the Y-axis
- Choose the aggregation (count, sum, avg, min, max)
- Choose the chart type (bar, line)
- The X-axis is always time; the histogram interval follows the global interval selector
Only numeric fields are accepted on the Y-axis — float fields are validated via a stats aggregation; text/keyword fields are rejected with an error message.
| Variable | Default | Description |
|---|---|---|
QUICKWIT_URL |
http://localhost:7280 |
Quickwit base URL |
PORT |
8080 |
Backend listen port |
VITE_SAVED_QUERIES_INDEX |
qwui |
Quickwit index used to store saved queries |
OIDC_ENABLED |
false |
Enable OIDC authentication |
OIDC_CLIENT_ID |
— | OIDC client ID |
OIDC_SECRET |
— | OIDC client secret |
OIDC_ISSUER |
— | OIDC issuer URL |
OIDC_REDIRECT |
— | OIDC redirect URL |
SESSION_SECRET |
— | Cookie session secret |
A Helm chart is available under helm/qwui/.
helm repo add qwui https://gitarns.github.io/qwui
helm repo update
helm install qwui qwui/qwui \
--set quickwit.url=http://quickwit:7280helm install qwui qwui/qwui \
--set image.repository=ghcr.io/gitarns/qwui-rust \
--set quickwit.url=http://quickwit:7280helm install qwui qwui/qwui \
--set quickwit.url=http://quickwit:7280 \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set "ingress.hosts[0].host=qwui.example.com" \
--set "ingress.hosts[0].paths[0].path=/" \
--set "ingress.hosts[0].paths[0].pathType=Prefix"| Value | Default | Description |
|---|---|---|
image.repository |
ghcr.io/gitarns/qwui-go |
Image to use (qwui-go or qwui-rust) |
image.tag |
chart appVersion |
Image tag |
quickwit.url |
http://quickwit:7280 |
Quickwit base URL |
replicaCount |
1 |
Number of replicas |
ingress.enabled |
false |
Enable ingress |
extraEnv |
[] |
Extra environment variables |
# Go backend
docker build -f Dockerfile.go -t qwui:go .
# Rust backend
docker build -f Dockerfile.rust -t qwui:rust .

