Installing Rúnakefli¶
Release candidate. Rúnakefli is feature complete but not yet proven in production. CI builds the packages and images below for every release; the apt suite
trixie-snapshotfollowsmain.
There are three ways to run Rúnakefli. All of them need a running ClickHouse® server (26.9 or later).
- Debian package (Debian 13 "trixie", amd64 and arm64): the easiest, with a hardened systemd unit.
- Container image (linux/amd64 and linux/arm64).
- Build from source: a static Go binary that you run under any supervisor.
Debian package (apt repository)¶
The repository is signed and published with this documentation. It has two
suites: trixie (releases) and trixie-snapshot (the latest build of main).
Packages are built for amd64 and arm64.
sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSLo /etc/apt/keyrings/runakefli-archive-keyring.asc \
https://runakefli.app/apt/runakefli-archive-keyring.asc
# compare the fingerprint with the one below
gpg --show-keys --with-fingerprint /etc/apt/keyrings/runakefli-archive-keyring.asc
sudo tee /etc/apt/sources.list.d/runakefli.sources <<'EOF'
Types: deb
URIs: https://runakefli.app/apt
Suites: trixie
Components: main
Signed-By: /etc/apt/keyrings/runakefli-archive-keyring.asc
EOF
sudo apt update && sudo apt install runakefli
The key's fingerprint is 74E9 21AC B040 DCC7 2C02 A144 1193 7B6E A8AF C649.
Use Suites: trixie-snapshot to follow main instead of the releases.
The package installs /usr/bin/runakefli, the man page, a hardened systemd
unit (dynamic user, state in /var/lib/runakefli) and
/etc/runakefli/runakefli.yaml. It does not start the service: edit the
config first (at least clickhouse.addr; see
the configuration reference). The file lists
every option with its default, commented out; uncomment and change what you
need, for example limits_config.retention_period (data is kept forever by
default). Then:
sudo systemctl enable --now runakefli
curl -fsS http://localhost:3100/ready
The default listen address is 127.0.0.1; change server.http_listen_address
to accept remote clients. Put the ClickHouse password in a systemd credential
as described in /usr/share/doc/runakefli/README.Debian. apt upgrade keeps
Rúnakefli current; each release also has its .deb files and a SHA256SUMS
file on the GitHub releases page.
Then continue with Step 5: first push and query.
Container image¶
podman run -d --name runakefli -p 3100:3100 \
-e RUNAKEFLI_CLICKHOUSE_ADDR=clickhouse.example.org:9000 \
ghcr.io/bzed/runakefli:latest
(docker run works the same.) The image is based on distroless static, runs
as a non-root user and listens on 0.0.0.0:3100. To reach it on another host
port, just map it (-p 8080:3100). To change the port inside the container,
set RUNAKEFLI_HTTP_LISTEN_PORT and map that port (-e
RUNAKEFLI_HTTP_LISTEN_PORT=8080 -p 8080:8080); it must be above 1023, as the
container runs as non-root. The built-in config reads these variables:
RUNAKEFLI_HTTP_LISTEN_PORT (default 3100), RUNAKEFLI_CLICKHOUSE_ADDR (default clickhouse:9000),
RUNAKEFLI_CLICKHOUSE_DATABASE (runakefli), RUNAKEFLI_CLICKHOUSE_USER
(default), RUNAKEFLI_CLICKHOUSE_PASSWORD and RUNAKEFLI_CLICKHOUSE_PASSWORD_FILE
(both unset: no password).
ClickHouse password¶
The quick way is -e RUNAKEFLI_CLICKHOUSE_PASSWORD=secret. Environment
variables show up in docker inspect and /proc, and the value must not
contain a single quote, so prefer a file: mount it read-only and point
RUNAKEFLI_CLICKHOUSE_PASSWORD_FILE at it. Set only one of the two; setting
both is a startup error.
install -m 0600 /dev/null clickhouse-password
printf '%s' 'secret' > clickhouse-password # a trailing newline is stripped
podman run -d --name runakefli -p 3100:3100 \
-e RUNAKEFLI_CLICKHOUSE_ADDR=clickhouse.example.org:9000 \
-e RUNAKEFLI_CLICKHOUSE_USER=runakefli \
-e RUNAKEFLI_CLICKHOUSE_PASSWORD_FILE=/run/secrets/clickhouse-password \
-v ./clickhouse-password:/run/secrets/clickhouse-password:ro \
ghcr.io/bzed/runakefli:latest
The container runs as nonroot (uid 65532), so the file must be readable by
that user (with rootless podman, add :U to the volume or chown the file).
With podman or Docker secrets, use --secret clickhouse-password (podman) or
a Compose/Swarm secrets: entry, which appear under /run/secrets/.
For anything else, mount your own config file (the image runs with
-config.expand-env=true) and set password_file: in it:
podman run -d -p 3100:3100 \
-v ./runakefli.yaml:/etc/runakefli/runakefli.yaml:ro \
-v ./clickhouse-password:/run/secrets/clickhouse-password:ro \
ghcr.io/bzed/runakefli:latest
Tags: X.Y.Z and X.Y for each release, and latest. Build it yourself with
make docker (podman or docker) and test it with make docker-test, which
starts a throwaway ClickHouse, pushes a line and queries it back.
Build from source (manual install)¶
The steps below build the static binary and run it under systemd.
Prerequisites¶
- A running ClickHouse server (26.9 or later), single-node or cluster.
The configuration reference covers
the keys you will set. For a single server on
localhost:9000, the default ClickHouse container image works out of the box. - Go (only needed for the build step; the binary is static —
CGO_ENABLED=0— and needs no Go toolchain or shared libraries at runtime). - systemd (or an equivalent process supervisor; the unit file below is
systemd-specific, but the same
ExecStartline works under any supervisor).
Step 1 — Build the binary¶
From the repository root:
make build
This produces bin/runakefli, a static binary. The Makefile adds
version/revision/date ldflags so runakefli -version reports the build.
An equivalent one-liner:
CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o bin/runakefli ./cmd/runakefli
Copy bin/runakefli to its final location, e.g. /usr/local/bin/runakefli.
Step 2 — Write the config¶
Create /etc/runakefli/runakefli.yaml. A minimal working config for a
single ClickHouse server on localhost:9000 with the default default
user (no password):
server:
http_listen_port: 3100
clickhouse:
addr: ["localhost:9000"]
database: runakefli
username: default
If your ClickHouse user has a password, either set password: in the
config (not recommended for production) or, better, write the password to a
0o600 file and reference it with password_file::
clickhouse:
addr: ["localhost:9000"]
database: runakefli
username: default
password_file: /run/credentials/runakefli.service/runakefli.clickhouse-password
The full key-by-key reference is in Reference → Configuration.
Step 3 — Create the systemd unit¶
Create /etc/systemd/system/runakefli.service:
[Unit]
Description=runakefli — Loki HTTP API on ClickHouse
After=network.target clickhouse.service
[Service]
Type=exec
User=runakefli
Group=runakefli
ExecStart=/usr/local/bin/runakefli -config.file=/etc/runakefli/runakefli.yaml
Restart=on-failure
RestartSec=5
# Hardening. Adjust the ReadWritePaths lines to your actual paths:
# - the ruler storage dir, if you enable the ruler
# - the delete WAL dir, if you enable the ruler with remote write
ProtectHome=true
ProtectSystem=full
NoNewPrivileges=true
# If you use password_file with a systemd-credentials path, uncomment:
# LoadCredentialEncrypted=runakefli.clickhouse-password:/run/credentials/runakefli.service/runakefli.clickhouse-password
Create the service user (no login):
sudo useradd --system --user-group --home-dir /var/lib/runakefli \
--no-create-home --shell /usr/sbin/nologin runakefli
sudo mkdir -p /etc/runakefli /var/lib/runakefli
sudo chown runakefli:runakefli /etc/runakefli /var/lib/runakefli
If you use password_file with a plain file (the default in the config
above), create it with the right owner and mode:
sudo install -m 600 -o runakefli -g runakefli /dev/null \
/run/credentials/runakefli.service/runakefli.clickhouse-password
# then write the password into that file
(If you prefer, the same path can be supplied via systemd credentials with
LoadCredentialEncrypted= / SetCredentialEncrypted=; the config key is
the same either way.)
Step 4 — Start and health-check¶
sudo systemctl daemon-reload
sudo systemctl enable --now runakefli
systemctl status runakefli
Health checks (both are served by Rúnakefli, both are Loki®-identical paths where applicable):
# readiness — returns the literal text "ready\n" with HTTP 200
curl -fsS http://localhost:3100/ready
# build info — returns JSON with version, revision, branch, build date, Go version
curl -fsS http://localhost:3100/loki/api/v1/status/buildinfo
Step 5 — First push and query¶
Push one entry (Loki's push JSON shape):
curl -fsS -X POST http://localhost:3100/loki/api/v1/push \
-H 'Content-Type: application/json' \
-d '{
"streams": [
{
"stream": {"app": "demo", "env": "dev"},
"values": [["1700000000000000000", "hello from runakefli"]]
}
]
}'
Query it back (instant LogQL query; the timestamp is in nanoseconds since the epoch, as Loki expects):
curl -fsS 'http://localhost:3100/loki/api/v1/query?query=%7Bapp%3D%22demo%22%7D&limit=10'
(%7B/%22/%7D are URL-encoded {/". The query is {app="demo".)
Expected response shape (Loki's loghttp JSON, served unchanged):
{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {"app": "demo", "env": "dev"},
"values": [["1700000000000000000", "hello from runakefli"]]
}
]
}
}
Upgrading¶
Rebuild the binary, stop the unit, replace the binary, restart:
sudo systemctl stop runakefli
sudo install -m 755 -o root -g root bin/runakefli /usr/local/bin/runakefli
sudo systemctl start runakefli
Note: schema changes ship with a migration (
-schema.migrate, see below; from schema version 2 it only alterslogs: no copy, no extra disk space, and existing data is not re-indexed). Rúnakefli is a release candidate: back up the ClickHouse database before upgrading if your data matters.
Uninstall¶
sudo systemctl disable --now runakefli
sudo rm /etc/systemd/system/runakefli.service
sudo systemctl daemon-reload
sudo rm /usr/local/bin/runakefli
# drop the ClickHouse database (irreversible):
# clickhouse client --query "DROP DATABASE runakefli"
Upgrading to 0.9¶
0.9 changes the ClickHouse schema (retention is no longer stored per row, see Operations). A 0.9 instance refuses to start against a database of an older version and exits 1 with a message naming the migration. Migrate once:
- Stop every Rúnakefli instance.
-
Run the migration:
runakefli -config.file /etc/runakefli/runakefli.yaml -config.expand-env -schema.migrateIf the ClickHouse password is a systemd credential (
password_file: ${CREDENTIALS_DIRECTORY}/runakefli.clickhouse-password), a shell has noCREDENTIALS_DIRECTORYand the command fails withopen ${CREDENTIALS_DIRECTORY}/...: no such file or directory. Run it as a transient unit that imports the credentials, asrunakefli.servicedoes:systemd-run --pty --wait --collect -p ImportCredential='runakefli.*' \ /usr/bin/runakefli -config.file /etc/runakefli/runakefli.yaml -config.expand-env -schema.migrate(
-config.expand-envis needed in both forms: it expands the${CREDENTIALS_DIRECTORY}in the configuration.)The ClickHouse user needs more rights for the migration than for normal operation (
DROP TABLE,SELECTonsystem.disks); see ClickHouse user and grants.It copies each table, so ClickHouse needs free disk space for a copy of the largest table; the migration refuses to start with less. It can be run again after a crash. 3. Start the instances.
Upgrading to 0.11¶
0.11 adds schema version 3 (structured-metadata and escaped-ASCII indexes on
logs). Run the same migration (stop every instance, -schema.migrate, start).
From version 2 it only alters logs: no copy and no extra disk space. Existing
data is not re-indexed; the new indexes cover parts written or merged after the
migration.
ClickHouse cluster (optional)¶
For a sharded/replicated ClickHouse cluster (e.g. 2 shards × 2 replicas),
set these in the clickhouse: block:
clickhouse:
addr: ["ch1:9000", "ch2:9000", "ch3:9000", "ch4:9000"]
database: runakefli
username: default
cluster: runakefli # the remote_servers name in ClickHouse
replicated: true # Replicated*MergeTree; false = shard without replication
Rúnakefli creates logs_local / streams_local Replicated*MergeTree
tables ON CLUSTER and Distributed logs / streams tables over them,
sharded by (tenant, stream_id). Inserts use distributed_foreground_insert
and (when replicated) insert_quorum, so a push is acknowledged only when
the rows are on a majority of replicas. Full cluster setup (Keeper,
remote_servers, replica paths) is a ClickHouse operations task; see the
ClickHouse documentation
and the configuration reference
for the Rúnakefli-side keys.
On a cluster the ClickHouse user needs a few extra grants (CLUSTER, REMOTE,
TABLE ENGINE ON Distributed); see
ClickHouse user and grants.