BlogGuides

How to self-host Quickwit on your own VPS without Docker

Run Quickwit 0.9.1, the open-source log search engine, on your Ubuntu server with ox and no Docker: a Rust build and Postgres, from the config that ran.

On this page

To self-host Quickwit you need an Ubuntu server, a fork of the Quickwit repo with one ox.toml file added, and no variables to set. In our run the first deploy took about 23 minutes, most of it compiling Quickwit's Rust code, and ox set up the Postgres database where Quickwit keeps its index records.

How we tested On Oct 17, 2026, ox deployed Quickwit v0.9.1 from commit 962685f5 on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory. The run then redeployed it, backed it up and rebooted the server. Then a separate test agent, one that did not write the config, used the API to make an index, add a document and find it by search, in the API and in the web UI, and checked the app's services and its backup.

Quickwit is an open-source search engine for logs and traces. You make an index, send it JSON documents and search them with a query language through a REST API. Its README also lists an Elasticsearch-compatible API and OpenTelemetry and Jaeger support. The code is Apache 2.0, written in Rust, and builds into one binary with a web UI inside. The UI at /ui/ has a Query editor and pages for indexes, the cluster and the node. It is for looking. You make indexes and add documents through the API.

ox deployed Quickwit v0.9.1 (commit 962685f5) on Oct 17, 2026 on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory; the first deploy took 23 minutes and a redeploy took 13 minutes.

  • First deploy: 23 minutes, from adding the project until the app passed the run's first checks.
  • Redeploy of the same commit: 13 minutes, until the app passed the checks again.
  • Server: DigitalOcean, 8 vCPUs and 16 GB of memory (s-8vcpu-16gb), with this app alone on it. Every one of the 85 apps we deployed ran on this one size, so it was not picked for this app.
  • Memory in use after the reboot: 688 MB of 15986 MB, for the whole server: Ubuntu, the app, its services, Caddy and the ox agent. On a server with no apps, ox's tests on real servers hold the agent to 40 MB or less and Caddy to 50 MB or less.
  • Services ox runs for it: postgres.
  • Source: github.com/quickwit-oss/quickwit at v0.9.1, commit 962685f5.

What you need

  • An Ubuntu LTS server. The run used the size in the facts above and measured the memory there. The server compiles Quickwit itself, so it reaches the internet for Cargo crates and npm packages.

  • A GitHub account to fork quickwit-oss/quickwit, and an ox account, which you open by signing in with GitHub.

  • No outside account. The run kept the index files on the server's disk. Cloud storage for indexes, log shippers, the OpenTelemetry, Jaeger and Elasticsearch-compatible endpoints and sources such as Kafka were not tested.

The steps to install Quickwit on Ubuntu

In the ox dashboard press Add a server and run the command it shows as root on your Ubuntu machine. It installs the ox agent and Caddy.

Fork quickwit-oss/quickwit and start from commit 962685f5, titled Release 0.9.1, the one the run deployed. Add the ox.toml shown below at the top of the repo, then commit and push. The config asks for no variables, so there is no env file. With the ox CLI signed in, run:

Shell
ox new <you>/quickwit
ox review quickwit
ox deploy quickwit --wait

The project takes the repo's name. ox review prints the plan and exits 0 when nothing is left to answer. ox deploy --wait streams the first deploy to its end. The quickstart shows the same steps in the browser.

When the deploy is green, open the address ox shows. It redirects to the UI's Query editor. The run used this preview address, and a domain of your own was not tested. The domains page covers adding one. Quickwit has no sign-in, so the test agent went straight to the API:

Shell
SITE=https://<the address ox shows>
curl -s $SITE/health/readyz
curl -s $SITE/api/v1/version
curl -s -X POST $SITE/api/v1/indexes -H 'Content-Type: application/json' \
  --data '{"version":"0.9","index_id":"verifynotes","doc_mapping":{"field_mappings":[{"name":"body","type":"text"}]}}'
curl -s -X POST "$SITE/api/v1/verifynotes/ingest?commit=force" \
  -H 'Content-Type: application/json' --data-binary '{"body":"hello quickwit from the verifier"}'
curl -s "$SITE/api/v1/verifynotes/search?query=body:quickwit"

The readiness route answers 200 with true, and the version route reports 0.9.1. The ingest call reports num_ingested_docs 1, and the search returns the document with num_hits 1. The Query editor finds it too: pick the index, type body:quickwit and press Run. The Indexes page also lists otel-logs-v0_9 and otel-traces-v0_9, which Quickwit made itself at its first start.

The test agent added commit=force, which commits at once. Without it a new document is searchable only after its split commits, by default up to 60 seconds later. Quickwit has no accounts or keys, so anyone who has the project's address can use the API. The run put nothing in front of it.

The ox.toml, line by line

TOML
# Quickwit 0.9.1, one node: the REST API and the UI on $PORT, the
# metastore in the project's postgres, the index files in the kept folder.
# The build follows the repo's Dockerfile (UI first, embedded into the
# binary, then cargo with the release feature set).
packages = ["clang", "llvm", "cmake", "pkg-config", "protobuf-compiler", "libssl-dev"]

[app]
# QW_DATA_DIR is the kept folder's stable place, not the release's link to
# it: the metastore records each index's absolute path, and a release folder
# is pruned three deploys later. gRPC defaults to REST+1, which can be the
# next project's port; 7281 is Quickwit's own default and outside ox's range.
start = 'export QW_LISTEN_ADDRESS=127.0.0.1 QW_REST_LISTEN_PORT="$PORT" QW_GRPC_LISTEN_PORT="${QW_GRPC_LISTEN_PORT:-7281}" QW_DATA_DIR="$OX_DATA_DIR/qwdata" QW_METASTORE_URI="$DATABASE_URL"; exec .ox/bin/quickwit run --config config/quickwit.yaml'
health = "/health/readyz"
# One data folder, one WAL and one fixed gRPC port: two nodes at once
# would fight over them.
stop_before_start = true

[build]
install = ""
# The target folder lives in HOME so a redeploy rebuilds only Quickwit's
# own crates, and the release keeps the binary alone.
commands = [
  "cd quickwit/quickwit-ui && touch .gitignore_for_build_directory && NODE_ENV=production make install build",
  "cd quickwit && CARGO_TARGET_DIR=\"$HOME/.cache/quickwit-target\" cargo build -p quickwit-cli --features release-feature-set --bin quickwit --release",
  "mkdir -p .ox/bin && cp \"$HOME/.cache/quickwit-target/release/quickwit\" .ox/bin/quickwit && .ox/bin/quickwit --version",
]
migrate = ""

[services]
postgres = {}

[storage]
keep = ["qwdata"]

[tools]
rust = "1.96"
node = "24"
yarn = "1.22.22"

The file follows the repo's Dockerfile: the UI first, embedded in the binary, then Cargo with the release feature set.

  • packages lists the Dockerfile's build tools (clang, llvm, cmake, protobuf-compiler, libssl-dev) plus pkg-config.

  • start sets five Quickwit variables and runs the built binary with exec, so ox's stop signal reaches it. quickwit run starts every service in one process, using the repo's own config/quickwit.yaml.

  • The listen address stays on loopback, where only Caddy reaches it, and the REST port is the $PORT ox hands out, so the API and the UI answer there.

  • The gRPC port would default to the REST port plus one, which on a shared server can be another project's port. The line fixes 7281 instead, Quickwit's usual gRPC port and outside the range ox hands out.

  • QW_DATA_DIR is the project's data folder. Quickwit records each index's absolute path in its metastore, and ox deletes old release folders, so the data cannot live inside a release.

  • QW_METASTORE_URI keeps the metastore, Quickwit's record of its indexes, in the project's Postgres.

  • health is Quickwit's readiness route. ox switches to a new release only after it answers.

  • stop_before_start is on because one data folder, one write-ahead log and one gRPC port cannot serve two copies. Each redeploy has a short gap while the old copy stops. The first deploy has none.

  • install = "" skips ox's own install step, since the build commands do the installing.

  • The first build command is the Dockerfile's UI step. Yarn installs from the frozen lockfile and builds the UI into the folder that Quickwit's Rust code embeds. The touch makes a file the UI's build script copies.

  • The second runs cargo build with release-feature-set, which includes the Postgres metastore. It runs in the quickwit folder so Cargo reads .cargo/config.toml and its tokio_unstable flag. CARGO_TARGET_DIR sits in the project's home, so a redeploy rebuilds only Quickwit's own crates.

  • The third copies the binary to .ox/bin and prints its version.

  • migrate is empty because Quickwit creates and updates its own Postgres tables at start.

  • [services] asks for Postgres, the metastore's database. [storage] keeps qwdata, so the data folder survives deploys and goes into backups.

  • [tools] pins Rust 1.96 (the repo's rust-toolchain.toml), Node 24 (the Dockerfile's UI stage) and Yarn 1.22.22 (the UI's package.json).

There are no workers or cron jobs. Indexing, merging and clean-up run in the one process, the only unit the run's check found.

This config asks for no variables.

ox provides the three values the start line reads: PORT, OX_DATA_DIR and DATABASE_URL, which comes from the Postgres service. That is why the review had nothing to ask. QW_GRPC_LISTEN_PORT is the one name you could set yourself, and the run set none. See variables.

Postgres, updates and backups

Quickwit gets its own database and user on the server's Postgres, which you can browse in the Services tab or with ox explore quickwit postgres. It holds only the metastore. The index files sit under qwdata.

To move to a newer release, merge its commit into your fork and push, or run ox deploy quickwit --wait. Quickwit's upgrade page says the move to 0.9 ran SQL migrations on the metastore at first start and that going back needs a metastore backup. A newer release and a rollback were not tested.

The run's backup came from Back up now on the Backups tab: a Postgres dump of the metastore and a tar.gz of the kept qwdata folder. ox backs up both each day too. Run ox backups quickwit to list them. A restore was not tested. The backups page covers copies in an S3 bucket, and self-hosting with ox lists more apps.

Frequently asked questions

Do I need Docker to self-host Quickwit?

No. Quickwit publishes a Docker image and release tarballs, but it compiles to a single binary. ox builds that binary on your server from your fork and runs it as a system service beside Postgres. An update is a push or one deploy command.

Does Quickwit have a login or an admin account?

No. Quickwit has no accounts, passwords or API keys, so there is no first user to create. The test agent made an index, added a document and searched for it with plain requests. Anyone who has the project's address can use the API, and the run put nothing in front of it.

Does Quickwit need Postgres?

Quickwit's own default metastore is a folder of files. This setup uses Postgres instead: ox sets up the database, and Quickwit creates its tables the first time it starts. Postgres holds only the metastore. The index files sit in the kept qwdata folder, and the run's backup covered both.

Quickwit makes a document searchable only after it commits the split, which by default happens up to 60 seconds after the split starts (commit_timeout_secs). Add commit=force to the ingest request and it commits and waits before it answers. commit=wait_for waits for the normal commit. The test agent used commit=force.

How much memory does self-hosted Quickwit use?

The facts above give the memory in use on the run's server after its reboot. That figure covers the whole machine: Quickwit, Postgres, Caddy and the ox agent. The build also compiles Rust on the server. ox works on any server from 1 GB up and tells you when memory is why an app stopped.

New to running apps on your own server? How ox self-hosts your apps shows what ox sets up on your server.

Keep reading

Deploy to a server you own

ox sets up systemd, Caddy and PostgreSQL on your Ubuntu server and deploys on every push. Free for your first project.

Sign up with GitHub Read the docs