Repository navigation
RunR 0.6.6
RunR is an open-source, self-hosted serverless platform on Kubernetes and Knative: functions, containers, sandboxes, WebAssembly and durable workflows, with an API, a web console, the runr CLI, SDKs, a Terraform provider and a GitHub Action. You install it on infrastructure you operate, connected or air-gapped. It is developed by BRF Teknoloji and released under the MIT License.
0.6.6 is a security and maintenance release of 0.6.5. A caller can no longer forge what RunR vouches for under another spelling: every region Traefik now deletes request headers whose names alias another one, such as X_Runr_Caller next to X-Runr-Caller, with the Traefik chart 42.3.0 (Traefik v3.7.17). Kata Containers moves to 4.3.1, and Knative 1.22, 1.23 and 1.24 bring new installs to Kubernetes 1.34 and 1.35. Zot 2.1.23 and kube-prometheus-stack 92.18.0 come to new regions, and to existing ones by a command of the operator. An internal registry on S3 runs, and the versions of Knative on offer are the ones that install. Reverb refuses client events, and the console and the API get smaller fixes.
Two things to read before upgrading: the upgrade stops the running Kata pods of every region with Kata, and a request header with _ or . in its name no longer reaches a project.
Install
- Kubernetes (Helm): the chart comes from BRF's public Helm repository, without credentials:
See the README and docs/install.md;
helm repo add brf-tech https://helm.brf.sh helm upgrade --install runr brf-tech/runr --namespace runr --create-namespace --set domain=runr.example.com
helm show values brf-tech/runrlists every value. A pinned install adds--version 0.6.6; the API and UI images, the region agent and the event gateway follow the chart version. - Single host (Docker Compose, also air-gapped): compose/README.md, from a checkout of the release tag (
git clone -b v0.6.6 https://github.com/BRF-Tech/runr.git). - CLI:
curl -fsSL https://runr.sh/install.sh | shon Linux and macOS. The archives of every platform are at runr.sh/dl (https://runr.sh/dl/cli/v0.6.6/) and attached below; check them againstchecksums.txt(docs/cli.md#install). - Terraform / OpenTofu:
source = "brf-tech/runr",version = "~> 0.6", from the Terraform Registry;terraform initchecks the archive againstSHA256SUMS, signed with the RunR release key (key ID2588846B241BC095). runr.sh serves the same archives underrunr.sh/brf-tech/runr, for OpenTofu and for configurations that use that address (docs/terraform.md#install). - GitHub Action:
uses: brf-tech/runr/actions/deploy@v0.6.6; it downloads the CLI from runr.sh (docs/github-action.md). - Images: every image is published under
ghcr.io/brf-tech/runrby release-images.yml, for linux/amd64 and linux/arm64, and pulls anonymously.
The documentation is at runr.sh/docs and in the documentation index here.
Upgrading from 0.6.5
Upgrade in place with helm upgrade of the running release; never uninstall and install again, and back up the database first (Procedure in docs/upgrade.md). From 0.6.5 there is no database migration, the images keep their names, and nothing has to be set, but two changes below need a decision first (0.6.5 → 0.6.6).
helm repo update brf-tech
helm upgrade runr brf-tech/runr --version 0.6.6 -n runr --reuse-values- Running Kata pods stop on each node, so plan a quiet hour. On every region with Kata,
region:reconcilere-runs kata-deploy one node at a time. Its cleanup removes/opt/kata, and containerd cannot take back a task whose shim is gone, so the Kata pods of that node stop. A project's pod starts again by itself on 4.3.1 when the node is done: clh, qemu and Dragonball workloads were down for about 6 minutes in a test on a Kind cluster. A Sandbox API sandbox ends, and RunR closes it as failed. Pods under runc and gVisor keep running. Most of the 6 minutes is the image pull, so runcrictl pull quay.io/kata-containers/kata-deploy:4.3.1on the Kata nodes before the upgrade (Kata Containers 4.3.1). - A request header with
_,.or another such character in its name no longer reaches a project. The region Traefik deletes it on every entry point: a client that sendsX_Request_Idorapi_keygets its normal answer, but the project does not get that header, and nothing is logged. Rename such headers to dashes before the upgrade; gRPC metadata keys count too (Request headers with aliasing names). knative.traefikAliasHeadersStrategy: keepis a stopgap for an app that cannot rename its headers in time. The default isdelete.keepreopens the hole: an underscore or dot alias of the identity headers can reach workloads on servers that fold header names. The value is install-wide, andregion:reconcilewarns on every run while regions keep such headers.- The region Traefik moves to chart 42.3.0.
region:reconcileupgrades existing regions with their own values: the Traefik pods roll once without an interruption, and a failed upgrade rolls itself back. A region whose nodes pull through a mirror of your own needsghcr.io/brf-tech/runr/reqs/traefik:v3.7.17there first, andknative.traefikImageset to it (Region Traefik chart 42.3.0). A Traefik below chart 40, a cluster below Kubernetes 1.30 and an olderknative.traefikImagecannot take the header fix, andregion:reconcilewarns about them. - The region agent and the event gateway move to 0.6.6. As in 0.6.0 to 0.6.5, they run at the installed version: after
helm upgradereturned, the Jobrunr-region-reconcile-<revision>moves the regions' agents, queue consumers and topic publishers fromagent:0.6.5/queue-consumer:0.6.5toghcr.io/brf-tech/runr/agent:0.6.6andqueue-consumer:0.6.6; each rolls once. A region that pulls through a mirror of your own needs these two images there before the upgrade (Order of operations). - Zot, kube-prometheus-stack and Knative do not move by themselves, because each restarts something a region depends on.
php artisan registry:upgrade-zot <region-id>moves a registry to Zot 2.1.23,--dry-runfirst;php artisan cluster:patch-observabilitymoves a region to kube-prometheus-stack 92.18.0, security rebuilds worth taking soon; a region stays on its Knative until someone upgrades it, one minor at a time (Registry, monitoring and Knative). - Three changes are visible to API and CLI users.
compatible-versionslists four minors at most,POST install-servinganswers 422 for aversionoutside that list, andrunr regions knative install REGION servingwithout--versionfollows the running operator. bootstrap.regionReconcileSecondsis 4800 by default. An upgrade with--reuse-valueskeeps 3600 unless you set it: no step is cut, and what the deadline leaves in the worst case is done by the next attempt.- Reverb refuses client events. RunR uses none.
REVERB_APP_ACCEPT_CLIENT_EVENTS_FROM=membersorallturns them back on (security.md). - Back to 0.6.5: a plain
helm rollback(orhelm upgradewith the 0.6.5 chart where only one package version is kept) on the same database; there is no migration to undo. The region Job of the 0.6.5 chart moves the region images back to 0.6.5 and re-runs kata-deploy with 3.28, which stops the Kata pods once more. Regions keep the Traefik chart 42.3.0, which 0.6.5 does not touch. - Terraform provider 0.6.6: its resources and data sources are the ones of 0.6.5.
From 0.6.4, upgrade straight to 0.6.6 and read 0.6.4 → 0.6.5 as well: 0.6.5 adds the notification_user_states migration, the Helm value notifications.retentionDays, and its daily task deletes notifications older than 90 days. From 0.6.3, also read 0.6.3 → 0.6.4 as well: 0.6.4 adds the captcha_settings migration and the Helm values captcha.*, and its first upgrade rolls the pods once. From 0.6.2, also read 0.6.2 → 0.6.3; from 0.6.1, also 0.6.1 → 0.6.2. From 0.6.0, also read 0.6.0 → 0.6.1 first: before a rollback to 0.6.0, let the running deploys finish (Rolling back to 0.6.0). From 0.5.10, read 0.5.10 → 0.6.0 in docs/upgrade.md first — in particular what to decide before the upgrade — and the notes of 0.6.0 to 0.6.5 in CHANGELOG.md; everything said there applies. From 0.5.9 or earlier, read the notes of the releases in between as well; from 0.5.8 or earlier, add --set api.image.tag= --set ui.image.tag= to that upgrade once (step 3 of the Procedure).
Changes in 0.6.6 (2026-11-06)
Upgrade notes
- Kata Containers moves to 4.3.1, and running Kata pods stop.
region:reconcilere-runs
kata-deploy on every region with Kata: the container runtime restarts twice on each node, one node
at a time. Kata pods on a node stop while it is done: a project's pod starts again by itself on
4.3.1 (about 6 minutes in a test, most of it the image pull, which can be done first), and a
Sandbox API sandbox ends. Plan it for a quiet hour. See
docs/upgrade.md. - The region Traefik moves to chart 42.3.0 (Traefik v3.7.17).
region:reconcileupgrades existing
regions: the Traefik pods roll once without an interruption, and a failed upgrade rolls itself
back. A region whose nodes pull through a mirror of their own needs
ghcr.io/brf-tech/runr/reqs/traefik:v3.7.17there first, andknative.traefikImageset to it. See
docs/upgrade.md. - A request header with
_,.or another such character in its name no longer reaches a
project. The region Traefik deletes it on every entry point: a client that sendsX_Request_Id
orapi_keygets its normal answer, but the project does not get that header, and nothing is
logged. Rename such headers to dashes before the upgrade. gRPC metadata keys count too. See
docs/upgrade.md. knative.traefikAliasHeadersStrategy: keepis a stopgap for an app that cannot rename its headers
in time. The default isdelete.keepreopens the hole of the entry under Security: an underscore
or dot alias of the identity headers can reach workloads on servers that fold header names. The
value is install-wide, for every region;region:reconcilemoves the regions either way and warns
on every run while they keep such headers.bootstrap.regionReconcileSecondsis 4800 by default, for the time the Traefik chart's move can
take. An upgrade with--reuse-valueskeeps 3600 unless you set it: no step is cut, and what the
deadline leaves in the worst case is done by the next attempt.
Security
- A caller can no longer forge what RunR vouches for under another spelling. Traefik's ForwardAuth
replacesX-Runr-Callerand the gateway'sX-Runr-Consumerin their dash form only, and a backend
that folds header names into variables readsX_Runr_CallerorX.Runr.Calleras the same header:
a caller could pass itself off as another project on the internal network, or as another consumer
on a gateway route, towards a project served by PHP's built-in server or a similar CGI or WSGI
server (a test with a ForwardAuth showed it for PHP's built-in server and Python'swsgiref,
not for FrankenPHP or RunR's built-in runtimes). Every entry point now deletes such headers
before routing (global.aliasHeadersStrategy: delete,
GHSA-rf44-j88r-hh8c,
GHSA-x677-9fxg-v5c5),
on new regions and on existing ones as they move to chart 42.3.0;region:reconcilewarns about
a region that cannot (security.md).
Changed
- Region Traefik chart 42.3.0. New regions install the chart 42.3.0 with Traefik v3.7.17 instead
of 40.3.0 with v3.7.4, and need Kubernetes 1.30, RunR's minimum already. The new reconcile step
traefik-chartmoves existing regions: chart 41 renamed the logging keys and its schema refuses the
old ones, so--reuse-valuescannot cross it, and the step rewrites the release's own values
instead. Rendered with RunR's values the two charts give the same container arguments, RBAC and
Services. A Traefik of a chart before 40 and a cluster below Kubernetes 1.30 are left alone and
reported. - Kata Containers 4.3.1. RunR installs kata-deploy 4.3.1 instead of 3.28.0. clh, qemu and fc
stay on Kata's Go runtime (deprecated upstream since 4.0, still shipped and supported), Dragonball
stays on runtime-rs. Thekata-installerDaemonSet follows the upstream chart's daemonset mode:
kata-deploy no longer enters the host, so the pod mounts/opt/kata, systemd's private socket and
the other paths it reaches, not the node's root. A Dragonball guest stays 1 vCPU and 256 MiB plus
the pod's limits: runtime-rs now adds the limits tooverhead_vcpus/overhead_memory(32 MiB
by default), which RunR's drop-in sets. runtime-rs sizes the vCPUs itself, so the pod annotations
RunR wrote for that are gone, and workloads that still carry them keep working
(kata-hypervisors.md). - Knative 1.22, 1.23 and 1.24. A new install takes Knative 1.23 on Kubernetes 1.34 and 1.24 on
1.35 or newer (1.22 and 1.23 need Kubernetes 1.34, 1.24 needs 1.35, as in Knative's release
schedule); a cluster up to 1.33 gets what it got. The Serving step,GET /api/knative-regions/{id}/compatible-versionsandrunr regions knative versionsfollow, and an
installed region stays on its version until it is upgraded. A running Serving is always offered
its next minor, so a region left behind climbs one minor at a time
(regions.md). - kube-prometheus-stack 92.18.0 (Prometheus Operator v0.94.2, Prometheus v3.15.1, security
rebuilds of the two). Regions installed from now on run it; an installed region keeps its chart
untilphp artisan cluster:patch-observability, a change back to RunR's own monitoring or a new
Serving install moves it. Its upgrade needs no step of its own: the operator's CRDs are the same,
and the chart's pre-upgrade Job recreates the admission webhook's Secret askubernetes.io/tls
with the same certificates (regions.md). - Zot 2.1.23 (chart 0.1.130) for registries set up from now on: fixes of storage, garbage
collection and on-demand sync. From Zot 2.1.22 an on-demand sync that fails answers 503, and an
image in Docker's manifest formats syncs only withhttp.compat: [docker2s2]. RunR's config covers
both: Docker Hub, which answers 401 for a repository it does not know, is no longer asked for
RunR's own namespaces (runr-builds/,runr-runtimes/,runr-uploads/), so a build's first
push gets its 404, andhttp.compatis set. An installed Zot keeps its version
untilphp artisan registry:upgrade-zot <region-id>moves it: an operator's step, because it
restarts the registry, soregion:reconciledoes not. The release keeps its values and the
config.json that runs, with these two settings added; Helm rolls a failed upgrade back, an
upgrade a killed process left pending
is rolled back first, and--dry-runshows what would change
(regions.md). - The Kind e2e runs Kubernetes 1.34 and 1.35 (
gh workflow run e2e.yml -f suite=kind -f k8s=1.35.0), the versions that take Knative 1.23 and 1.24:e2e.ymlmoves to Kind v0.31.0, the
first release with their node images, and the reqs mirror carries them. A run on a host of your
own needs Kind v0.31.0 or later (development.md).
Fixed
- A Kata reinstall no longer promises that running pods keep running.
region:reconcile, the
install log and the console's warning said so, but kata-deploy's cleanup removes the Kata shim, and
containerd restarting without it drops the node's Kata tasks. They now say what happens: Kata pods
on the node stop, a project's pod starts again when the node is done, and a sandbox ends. A first
install is not affected. - The Knative versions on offer are the ones that install. The Serving step offered five
minors, but a Knative Operator holds four, its own and the three before it, and its chart does not
follow the minor chosen: the oldest on the list failed when the operator got it, with "the
manifests of the target version are not available to this release". The list now holds what the
operator that runs can install, or the one an install is about to pick, so an older operator
also no longer offers minors newer than its own. A Serving install without a version
(runr regions knative install REGION serving) takes the newest of that list instead of the
newest the cluster supports, andPOST /knative-regions/{id}/install-servingrefuses aversion
outside it with a 422. - RunR applies the Knative Serving CRDs it downloads. Every document of
serving-crds.yaml
opens with a licence comment, and the parser skipped a part that opens with a comment: the
operator install and the Serving upgrade applied no CRD at all, on every version, and said
nothing. The operator's own apply covered for it, later than the install expects: the region's
Traefik started before its Knative CRD existed. All twelve are now applied, also the empty
status: {}of each, which a plain PHP array would have sent as[]and the API server refused.
An uninstall takes back all of them, found by theirapp.kubernetes.io/name=knative-serving
label: Uninstall Operator removed six anduninstall-knativenone.uninstall-knativekeeps them
while Knative Services are left on the cluster, and Uninstall Serving keeps them
(regions.md). - An internal registry on S3 runs. Zot refused the config RunR gave it on S3 (dedupe on with no
remote database, no download directory for its sync), so such a registry never started; with
those two fixed it would still have gone to AWS, because the address was written under a key the
S3 driver does not read. RunR now turns dedupe off, downloads to the pod'semptyDir, and passes
the endpoint asregionendpointwith path-style addressing. No region needs a migration: none
could have run (regions.md). - The daily chart check reports only a version newer than RunR's.
ci/check-helm-releases.sh
reported the latest version of every chart without looking at the version RunR pins, so a chart
already at its pin got an issue (deps: loki-stack 2.10.3 released). It now reads each chart's
pin from its constant in the code and files nothing for a chart at its pin; a constant it cannot
read fails the check (development.md). - The notification bell shows the unread count from the start. The console asked for the count
only in the fallback poll, every 2 minutes while the WebSocket was down: with the socket up the
badge stayed empty after every page load and counted only the notifications that arrived live
(an installation with 533 unread showed none). The header now asks once when the console loads
and again after a sign-in or an organization switch, with the socket up or down; the socket and
the panel's reads and deletes keep the count from there, and the 2-minute poll still runs while
the socket is down. After a sign-out and another member's sign-in the badge shows that member's
count, never the previous one's. "Mark all as read" also shows when every notification the panel
lists is read but older ones are not. - The console's language: the first of the browser's languages it has. Without a choice of the
language switch the console looked only at the browser's first language: a browser set to
German, then Turkish, opened in English. It now takes the first ofnavigator.languagesthat is
Turkish or English, and English when none is; the switch's stored choice still wins, and no
installation setting changes the order. The sign-in page, the console after the sign-in, the
page'slangand the API'sAccept-Languagefollow it alike. (A headless browser started
without a locale reports the system's language: on a Turkish system it gets Turkish.) GET /api/versionshows a build's label as it is. EveryAPP_VERSIONthat did not start
withvgotmain-in front: the public demo'sdemo-0.6.5readmain-demo-0.6.5, and the
main-<sha>ofruntime/ci.shreadmain-main-<sha>. Only a bare commit id (7 to 40 hex digits,
the default ofcompose/deploy.sh) gets the prefix now; a tag,devand any other label stay
as they are (versioning.md).- No client events on the WebSocket.
config/reverb.phpleftaccept_client_events_from
unset, and Reverb then takes client events (client-*messages, Echo'swhisper) from any
connection, also one that has not subscribed to the channel. The console sends and listens for
none, so it ignored them; Reverb now refuses them (none, env
REVERB_APP_ACCEPT_CLIENT_EVENTS_FROM), and on a login several people share, such as a public
demo, no visitor reaches the others' consoles (security.md). - The demo data seeded twice is seeded once, and its list starts with the first sample. A second
runr:install --demo(DemoSeeder) on the same database added eleven more demo projects; it now
keeps the ones the demo region has. The project list (newest first) showed the samples backwards,
starting with the Node.js 18 function: it now reads in their order, so the first card the tours
open is the Python HTTP function with its rollback history. The demo data stays English and every
project keeps its static history (four versions, a deployment each, no build and no region
call); tests now check every text the seeder writes and the versions' code for Turkish, the
first project's deployments and a second seeding of a fresh database, as the demo's nightly
reset does. - The console's index.html is never cached, and a missing build file answers 404. The UI
image's Caddyfile (and the Compose gateway's) set the cache headers beforetry_filesrewrote
the path:/,/loginand every other console route got index.html without
Cache-Control: no-store, so a browser could keep it across an upgrade, and a chunk of the
previous build that a console opened before the upgrade asked for got index.html under the build
files' one-yearimmutableheader. The headers are now decided on the rewritten path: index.html
for every route and/branding-defaults.jsnever cached, the hashed files under/assets/for
a year (also their pre-compressed.br/.gz), a missing file under/assets/404 with
no-store. A console whose page chunk answers 404 loads again once (at most once a minute), now
with the new build.ci/check-caddyfiles.sh(CI) runs both Caddyfiles in Caddy and checks these
answers.