Run GKG as native Rust processes connected to NATS, Siphon, PostgreSQL, and ClickHouse from your GDK installation.
Working on
orbit-local, the ontology, language parsers, or docs only? You don't need GDK or any of the services below. See the Orbit Local development quickstart.
-
mise for tool version management
-
ClickHouse installed locally.
GDK does not download the ClickHouse binary for you. When
clickhouse.enabled: true, GDK templates the config and registers the service, but expects a binary to already exist atclickhouse.bin(default/usr/bin/clickhouse). If none is present, GDK silently skips the service — so install ClickHouse yourself first.On macOS, follow the terminal process instructions. After downloading, remove the binary from quarantine before running it:
xattr -d com.apple.quarantine clickhouse
On Linux, download the binary with the one-liner:
curl -sSL "https://clickhouse.com/" | sh
This downloads a
./clickhousebinary into the current directory. Either pointclickhouse.biningdk.ymlat it, or move it onto yourPATH(for example to/usr/bin/clickhouse);sudo ./clickhouse installis not required.Note: GDK's ClickHouse listens on port 9001, not the default 9000. Always pass
--port 9001when usingclickhouse clientto connect to the GDK instance. Runningclickhouse clientwithout--port 9001connects to a standalone ClickHouse instance if you have one installed. -
GDK with required services enabled:
Add the following to
$GDK_ROOT/gdk.yml:clickhouse: enabled: true nats: enabled: true siphon: enabled: true postgresql: host: localhost
Setting
postgresql.host: localhostmakes PostgreSQL listen on TCP, which Siphon requires for logical replication (GDK defaults to Unix sockets). -
PostgreSQL logical replication:
Edit
$GDK_ROOT/postgresql/data/postgresql.conf(andreplication.confif it exists):listen_addresses = 'localhost' wal_level = logicalThen restart PostgreSQL:
gdk restart postgresql -
ClickHouse setup:
Create the Rails ClickHouse config from the example, create the database, and run migrations:
cp $GDK_ROOT/gitlab/config/click_house.yml.example $GDK_ROOT/gitlab/config/click_house.yml clickhouse client --host localhost --port 9001 --query "CREATE DATABASE IF NOT EXISTS gitlab_clickhouse_development" cd $GDK_ROOT/gitlab && bundle exec rake gitlab:clickhouse:migrate
Then create the GKG graph database and apply the schema:
clickhouse client --host localhost --port 9001 --query "CREATE DATABASE IF NOT EXISTS \`gkg-development\`"Apply the graph schema using the helper script (it applies each statement individually since ClickHouse does not support multi-statement DDL execution):
scripts/apply-graph-schema.sh
The script defaults to
localhost:9001and databasegkg-development. Override with--host,--port, or--databaseflags, or setCLICKHOUSE_HOST,CLICKHOUSE_PORT,CLICKHOUSE_DATABASEenvironment variables. Run with--dry-runto preview statements without executing.Or skip both steps and run
mise run dev:setuplater (see Setup). -
Configure Siphon tables:
The set of replicated tables is driven by per-table YAML files in
$GDK_ROOT/gitlab/db/siphon/tables/. GDK reads them ongdk reconfigureand generates the entire Siphon config ($GDK_ROOT/siphon/config.yml) — both the producer and consumer sides — from that single source. In general you do not hand-writeconfig.yml; add or remove table files and let GDK regenerate it. (One exception, the hardcoded Prometheus port, is covered under Siphon Prometheus port conflict.)The GitLab repo already ships the tables the live indexing path needs, including the system-notes / commit-edge path:
notes,system_note_metadata,merge_requests,issues,users, androutes(the transform reads these to resolve note bodies, theactiondiscriminator, noteable and cross-reference targets, authors, andpath→ traversal-path lookups), alongsidenamespacesandprojects. Confirm the ones you need exist:ls $GDK_ROOT/gitlab/db/siphon/tables/Each file maps one PostgreSQL table to a ClickHouse
siphon_*target, for example$GDK_ROOT/gitlab/db/siphon/tables/notes.yml:table: notes database: main replication_targets: - name: clickhouse_main target: siphon_notes
To replicate a table that isn't shipped yet, add a file in the same shape, generate its ClickHouse migration, and run it:
cd $GDK_ROOT/gitlab bundle exec rails generate gitlab:click_house:siphon <table_name> bundle exec rake gitlab:clickhouse:migrate
Then regenerate the Siphon config and restart the service:
gdk reconfigure gdk restart siphon
For full details (multi-database support, re-syncing a single table) see the GDK Siphon how-to. The staging Siphon config lists the tables used in production.
-
Enable Knowledge Graph and JWT auth:
Add the
knowledge_graphsection to$GDK_ROOT/gitlab/config/gitlab.ymlunder theproduction:/development:block (e.g. near theelasticsearch:section):knowledge_graph: enabled: true
Protect
gitlab.ymlfrom being overwritten by adding it togdk.protected_config_filesin$GDK_ROOT/gdk.yml:gdk: protected_config_files: - gitlab/config/gitlab.yml
Restart Rails to auto-generate the JWT secret file:
gdk restart rails-web rails-background-jobs
This creates
$GDK_ROOT/gitlab/.gitlab_knowledge_graph_secretwhich the dev script reads automatically to configure the GKG webserver's JWT verifying key. Verify the file was created:ls $GDK_ROOT/gitlab/.gitlab_knowledge_graph_secretIf the file does not exist, restart Rails again. It may take a second restart for the secret to be generated.
Enable the feature flags:
cd $GDK_ROOT/gitlab bundle exec rails runner "Feature.enable(:knowledge_graph); Feature.enable(:knowledge_graph_infra)"
Enable namespaces for indexing:
cd $GDK_ROOT/gitlab bundle exec rails runner "Namespace.where(type: 'Group', parent_id: nil).find_each { |ns| Analytics::KnowledgeGraph::EnabledNamespace.find_or_create_by!(root_namespace_id: ns.id) }"
The Knowledge Graph UI is available at
https://<gdk-hostname>:<gdk-port>/dashboard/orbit.
Clone this repository somewhere accessible (for example, next to your
$GDK_ROOT directory). The GDK_ROOT variable in .env (see step 2) is how
GKG locates your GDK installation, so the two directories do not need to be
adjacent.
-
Install dependencies:
mise install
-
Configure environment:
cp .env.example .env
Edit
.envand setGDK_ROOTto the absolute path to your GDK installation. The script derives GDK service ports fromgdk.ymlautomatically, so you do not need to copy connection details into.env. -
Validate prerequisites:
mise run dev:check
-
Create graph database and apply schema:
mise run dev:setup
-
Start all services:
mise run dev
The GKG webserver is available at http://localhost:8090 (HTTP) and
localhost:50054 (gRPC) by default. Ports can be changed in .env.
This starts all three GKG runtime modes in the foreground:
- 1 webserver (HTTP + gRPC)
- 1 indexer
- 1 dispatcher (dispatch-indexing)
mise run dev runs these processes with prefixed output. Ctrl+C stops
everything.
Useful companion tasks:
mise run dev:check # validate prerequisites
mise run dev:setup # create graph DB + apply schema
mise run dev:status # show derived config
mise run dev:env # print env varsmise run gdk is also available as an alias.
On the first run, cargo compiles the full workspace which takes several
minutes. Subsequent runs use the cached build and start in seconds.
Port assignments can be overridden in the .env file if you want to run
multiple isolated local clusters on the same machine.
The dev script reads hostname, port, and https.enabled from gdk.yml to
derive GKG_GITLAB__BASE_URL. If your GDK has HTTPS enabled (for example
https.enabled: true with hostname: gdk.test and port: 3443), the script
automatically sets GKG_GITLAB__BASE_URL=https://gdk.test:3443.
For HTTPS to work, the GKG server's TLS stack (rustls via reqwest) must
trust the certificate. If you used mkcert to generate GDK certificates, run
mkcert -install to add the root CA to your system trust store.
Siphon's Prometheus port (8081) often conflicts with Elasticsearch. GDK
hardcodes this port when it generates $GDK_ROOT/siphon/config.yml and exposes
no gdk.yml knob for it, so changing it is the one case where you override the
generated file. If Siphon crash-loops with
listen tcp :8081: bind: address already in use, change the port:
prometheus:
port: 8082Then protect the file from being regenerated by adding siphon/config.yml to
gdk.protected_config_files in gdk.yml, and gdk restart siphon. Note that
while this file is protected, GDK will not pick up new table files until you
remove the protection and reconfigure.
NATS connection refused:
- Verify GDK NATS is running:
gdk status nats - Check if NATS port is accessible:
nc -zv localhost 4222
NATS limit_markers error:
- Update
NATS_VERSIONin$GDK_ROOT/support/makefiles/Makefile.nats.mkto a version >= 2.11 (example2.11.12) - Run
cd $GDK_ROOT && rm -rf nats/nats-server - Run
make nats-setup && nats/nats-server -version - Restart GDK:
gdk restart nats
ClickHouse connection issues:
ClickHouse exposes two ports: the native TCP port (9001 in GDK)
used by clickhouse client, and the HTTP port (8123) used for
health checks and REST-style queries.
- Verify ClickHouse is running:
gdk status clickhouse - Check HTTP port:
curl "http://localhost:8123/ping" - Check native port:
clickhouse client --host localhost --port 9001 --query "SELECT 1"
MEMORY_LIMIT_EXCEEDED errors from ClickHouse:
ClickHouse's max_server_memory_usage is a whole-host RSS limit, not a
per-query budget. GDK sets it from clickhouse.max_server_memory_usage in
gdk.yml (default 3 GB), and on a busy GDK the baseline RSS can already exceed
that cap — so even a trivial SELECT fails with
(total) memory limit exceeded ... (MEMORY_LIMIT_EXCEEDED).
GDK generates $GDK_ROOT/clickhouse/config.d/gdk.xml from gdk.yml, so editing
that file directly is overwritten on the next gdk reconfigure. Raise the limit
through gdk.yml instead:
clickhouse:
max_server_memory_usage: 8000000000Then apply it and restart:
gdk reconfigure
gdk restart clickhouseA value of 0 disables the absolute cap and falls back to a fraction of host
RAM (max_server_memory_usage_to_ram_ratio), which is a good choice when total
RAM, not a fixed number, is the right ceiling.
403 Forbidden on the /dashboard/orbit page but JWT auth works:
- The Knowledge Graph UI on the GDK (
/dashboard/orbit) requires a Premium or Ultimate license. - View instructions for configuring a license for the GDK: Configure a developer license in GDK
No data in graph:
- Check siphon services:
gdk status siphon - Verify
siphon_*tables have data:clickhouse-client --port 9001 -q "SELECT count() FROM siphon_projects" - Check GKG indexer output in the
mise run devterminal
mise install crashes with Rust toolchain errors:
If mise install fails with errors related to parallel Rust toolchain installs,
reinstall the stable toolchain manually:
rustup toolchain uninstall stable
rustup toolchain install stableThen re-run mise install.
Datalake connection errors in the indexer:
If the indexer logs errors like datalake query failed: client error (Connect),
verify that ClickHouse is running and accessible:
gdk status clickhouse
curl "http://localhost:8123/ping"Also confirm that the gitlab_clickhouse_development database exists and the
Siphon datalake tables have been created:
clickhouse client --host localhost --port 9001 --query "SHOW TABLES FROM gitlab_clickhouse_development"If the tables are missing, check that Siphon is running (gdk status siphon)
and has been configured correctly (see Configure Siphon tables).