Skip to content

Deployment & self-hosting

QueryPilot is designed to be self-hosted. The supported deployment today is the repository’s Docker Compose file, which runs four services plus optional profiles.

Service Image source Host port Internal port Health checks
postgres postgres:16-alpine POSTGRES_PORT (5432) 5432 pg_isready
api infrastructure/docker/api.Dockerfile API_PORT (8080) 8080 /health/live, /health/ready
worker infrastructure/docker/worker.Dockerfile None 8081 /health/live, /health/ready
web infrastructure/docker/web.Dockerfile WEB_PORT (3000) 3000 None
agent (profile agent) infrastructure/docker/agent.Dockerfile None 8082 /health/live, /health/ready
prometheus (profile observability) prom/prometheus PROMETHEUS_PORT (9090) 9090 None
grafana (profile observability) grafana/grafana GRAFANA_PORT (3001) 3000 None
ollama (profile ai) ollama/ollama OLLAMA_PORT (11434) 11434 None

api and worker wait for PostgreSQL to be healthy; agent waits for the API to be healthy.

Compose reads a .env file next to docker-compose.yml. The variables the Compose file uses:

Variable Default Notes
QUERYPILOT_ENCRYPTION_KEY None Required. 32 bytes, base64 or hex. openssl rand -base64 32.
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB querypilot Credentials of the bundled storage database. Change the password.
NEXT_PUBLIC_API_URL http://localhost:8080 API URL as seen from the browser, not from inside the Compose network.
QUERYPILOT_CORS_ORIGINS http://localhost:3000 Comma-separated web origins allowed to call the API.
LOG_LEVEL info debug, info, warn, error.
API_PORT, WEB_PORT, POSTGRES_PORT 8080, 3000, 5432 Host ports.
GRAFANA_PASSWORD admin Only with the observability profile.

Every other API/worker setting can be passed the same way; see the configuration reference.

  • Restrict network access. QueryPilot does not have user authentication yet (Roadmap). Expose the web app and API only on a trusted network, behind a VPN, or behind an authenticating reverse proxy.
  • Terminate TLS in front of the API and web app with a reverse proxy or load balancer. Agents send their bearer token on every request, so the agent → API hop should be HTTPS.
  • Set QUERYPILOT_PUBLIC_API_URL to the URL agents should use. It appears in the setup commands the UI generates; without it, the URL of the operator’s own request is used.
  • Set NEXT_PUBLIC_API_URL and QUERYPILOT_CORS_ORIGINS to your public API URL and web origin. Next.js inlines NEXT_PUBLIC_* variables into browser code when the web app is built, and the web client falls back to http://localhost:8080 when the variable is unset. If the browser still calls localhost:8080 after you change it, make the variable available at image build time and rebuild the web image.
  • Protect the encryption key. Store it in your secret manager and back it up. Without it, the stored database credentials cannot be decrypted and must be re-entered.
  • Change POSTGRES_PASSWORD and consider not publishing port 5432 on the host at all.
  • Back up the storage database (querypilot-postgres-data volume) like any other PostgreSQL.
  • Run agents close to the observed databases, not on the QueryPilot host, and give them the least-privilege role from PostgreSQL prerequisites.

The API applies pending migrations at startup, using an advisory lock so several API instances starting together do not race. To manage schema changes out of band instead:

QUERYPILOT_AUTO_MIGRATE=false

and run pnpm db:migrate with DATABASE_URL pointing at the storage database.

Settings can also come from a YAML file. Mount it into the api and worker containers and point QUERYPILOT_CONFIG_FILE at it. Precedence is: built-in defaults → configuration file → environment variables. Secrets are only ever read from the environment.

querypilot.yaml
deployment:
instanceName: production
privacy:
storeQueryText: true
agent:
intervals:
queryStatsMs: 30000
planCapture:
intervalMinutes: 30

Every service exposes Prometheus metrics at /metrics (API on 8080, worker on 8081, agent on 8082). The observability profile starts Prometheus pre-configured to scrape the API and worker, and Grafana:

Terminal window
docker compose --profile observability up -d

Health endpoints:

  • GET /health/live: the process is running. Never touches PostgreSQL, so a database outage does not get a healthy container killed.
  • GET /health/ready: required dependencies are available (non-200 otherwise).
  • GET /api/v1/health: the API’s detailed report, including optional subsystems.
Terminal window
git pull
docker compose up -d --build

The API migrates the storage database on startup. Agents and the API negotiate a protocol version at registration; upgrade the API before the agents.