Self-hosted service
openrundev/openrun avatar
openrundev/openrun

OpenRun keeps its app specs in another repo and its config in Starlark

Deployment platform for code-first internal tools. Deploy web apps declaratively, on a single-node or on Kubernetes, with OIDC/SAML auth and RBAC.

997 stars36 forksGoApache-2.0

At a glance

What is it?
OpenRun is a self-hosted Go deployment platform for web apps and internal tools: one binary that is also the web server, app config written as .star files, OAuth/OIDC/SAML with RBAC, and the same declarations reused when a workload moves from one Docker host to Kubernetes. Its edges are in nested Go modules, an opt-in test matrix, and a docs site that ships inside the code repo.
Who is it for?
OpenRun suits teams that already run one server and want app config, database accounts, TLS and access control to arrive from the same Git repository. Check the AppSpec list before assuming a framework deploys without a Dockerfile, confirm whether an imperative console change survives the next scheduled sync, and read the Apache-2.0 LICENSE alongside the ClaceIO copyright headers.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • openrundev/openrun997 ★ · +95
Star history
Repository2026-07-302026-11-14
openrundev/openrun902997

95 Stars in the last 107 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

App config is Starlark, and the spec templates sit in a separate repo

An app declaration in OpenRun is a small file ending in .star, and the examples directory holds one per framework: gradio.star, hono.star, nicegui.star, reflex.star, streamlit.star, todo.star, todo_derived.star and utils.star, alongside a todo/ directory and an async_action/ directory. The stated advantage over the usual approach is length rather than syntax, in a line that promises each app is a couple of lines of config instead of pages of YAML. What you do not get from the config is the framework knowledge itself: app specifications, the templates used to create apps, live in a separate appspecs repository, and sample apps live in yet another one. So the file that describes your app and the template that generates it are versioned in three places, this repository, appspecs and apps. The documentation site is the fourth, since the source for openrun.dev sits in the docs folder of this same repository.

Three ways to change an app, with no stated winner when they disagree

OpenRun counts three management paths and says they can be mixed freely. The recommended one is declarative: apps live in config files in Git, and a scheduled background sync creates new apps and updates existing ones as the config and code change.

bash
openrun sync schedule

The second is imperative, through the CLI, where `openrun app create` and `openrun app update` act on individual apps for trying things out and scripting. The third is the browser based management console, which handles apps, services, bindings, secrets, container views, audit events and server config. That third path is the interesting one: the console can create and edit the same objects the Git sync owns, and the file does not say which side wins when an app exists in the console but not in Git. A scheduled sync that updates existing apps as Git changes will find something in Git or it will not, and an object created only in the console has no declaration behind it at all.

The binary is the web server, so nothing sits in front of it

OpenRun is implemented as a web server and does not depend on an external one like Nginx or Traefik. The stated reason is twofold: it simplifies end-user usage, and being the layer that terminates traffic is what makes scale down to zero for app containers and OAuth, SAML or certificate based auth with RBAC possible in the first place. Routing is by domain or by path with automatic TLS, and the module list carries a certificate automation library alongside an HAProxy style zero downtime binary reload. The practical shape is a single binary you run on one server with Docker or Podman, or point at a Kubernetes cluster with the same config. The cost is equally concrete: if a web server you already operate is in front of your workloads, giving up that position means TLS termination, host routing and any proxy level policy all move into OpenRun, and the file does not describe a supported way to run behind one.

One container per app, no Compose, and AppSpec decides whether you write a Dockerfile

The supported unit is a web app that runs in a single container, with optional sidecar containers for background workers and companion services, and jobs for batch work. What is explicitly out is anything requiring multiple containers through Docker Compose. Between those two lines sits the AppSpec system, which is the part that decides your build burden. For frameworks with an AppSpec, named in the file as Streamlit, Gradio, FastHTML, NiceGUI, Shiny and Reflex, there is zero config deployment with no Dockerfile and no changes to the app source. For a framework with no AppSpec, a Dockerfile has to be present in the app source repo. The comparison with other platforms closes mid sentence, right after the words External service, so the rest of that answer is simply not in the file, and there is no statement about how migrations between those two categories are handled.

Service bindings hand every app its own database account

Instead of one shared database, a binding provisions an isolated Postgres, MySQL, SQLite or Redis account for a given app, with more databases reachable through binding providers. Isolation is the point being sold, and it is enforced by provisioning rather than by convention, which means credentials end up being issued per app. SQLite has a second, separate path: it is managed, with continuous Litestream replication to S3 and automatic restore, so an app with a local database file gets offsite copies without the app knowing. The Go module reflects this spread, pulling in Postgres and MySQL drivers, a Redis client, a Vault API client, AWS S3, Secrets Manager, SSM and ECR clients, Litestream and a hash based filesystem. Bindings are also a separate Go module, pkg/binding, versioned apart from the main binary, which is why the credential logic can move independently of the deployment engine.

Two nested modules pinned to the release tag, and a UI filter with no matching directory

go.mod declares module github.com/openrundev/openrun, requires Go 1.27.0, and carries a ClaceIO, LLC copyright header under Apache-2.0, as does the Makefile. Inside the same require block sit two dependencies on this project itself: github.com/openrundev/openrun/pkg/binding and github.com/openrundev/openrun/pkg/plugin, both pinned at v0.20.0, the same number as the current release. Nested modules and tagged releases therefore have to be moved together or the main build resolves an older copy of its own plugin interfaces. The Makefile spells out why the workspace file cannot be trusted for this: package lists are computed with GOWORK=off, because in workspace mode `go list -m` returns every workspace module and `go list ./...` crosses into the nested pkg/binding module, changing what gets built and linted. One wrinkle sits in the same file. Every package list runs through a filter that strips paths containing /ui/, and no ui directory appears among the top level entries of this repository.

The CLI test matrix switches on one backend per make variable

Integration coverage lives in a shell script, tests/run_cli_tests.sh, and the Makefile wires it up through variables rather than flags. The documented shape of a run is a make target plus backends:

bash
make int CONTAINER_COMMANDS=docker POSTGRES=1

The toggles are CONTAINER_COMMANDS, CONTAINER_TOOL, POSTGRES, POSTGRES_URL, MYSQL, MYSQL_URL, REDIS, REDIS_URL, SEAWEEDFS, S3_URL, KUBE_REGISTRY, KUBE_NAMESPACE, DR and SKIP_BUILD, and anything without its own variable goes through RUN_CLI_TEST_ARGS, which the Makefile describes as an escape hatch. Those values become the script's own flags, with --home always passed, and --container-commands, --container-tool, --postgres and --postgres-url added only when the corresponding variable is set. The consequence for a reader is that a green run means little on its own: what was exercised depends on which variables the run set, and a database or object store backend that nobody turned on is simply absent from the result.

v0.20.0 pairs Async Actions with an MCP server in the module list

The release cadence is quick and pre one point zero: v0.19.3 on 1 September 2026, v0.19.4 on 8 September, then v0.20.0 on 29 September, whose note reads Async Actions and auto MCP. The tree supports both halves of that note. An examples/async_action/ directory sits beside the .star app files, so asynchronous work has a worked example rather than only a changelog line, and the module requirements include the official Model Context Protocol Go SDK at v1.7.0, which is what an auto configured MCP endpoint would be built on. The same dependency list also shows an agent protocol SDK, a plugin system library, an embedded frontend toolchain in esbuild and BuildKit for image builds, and Git operations through a pure Go implementation, so the binary carries its own front end assets, its own builder and its own Git client rather than shelling out for each. A .goreleaser.yaml at the top level handles the tagging that the nested modules have to follow.

Editorial conclusion

OpenRun suits teams that already run one server and want app config, database accounts, TLS and access control to arrive from the same Git repository. Check the AppSpec list before assuming a framework deploys without a Dockerfile, confirm whether an imperative console change survives the next scheduled sync, and read the Apache-2.0 LICENSE alongside the ClaceIO copyright headers. It is the wrong shape for Compose fans and for anyone who wants a reverse proxy they already run to stay in charge of TLS.

Frequently asked questions

What does an app need before OpenRun can deploy it?

A web app that runs in a single container, plus its config in the Git repository. For the frameworks listed with an AppSpec, namely Streamlit, Gradio, FastHTML, NiceGUI, Shiny and Reflex, no Dockerfile and no source changes are needed; otherwise a Dockerfile must be present in the app source repo.

Can OpenRun deploy an app that needs Docker Compose?

No. Apps requiring multiple containers using Docker Compose are outside what it supports, although sidecar containers for background workers and companion services, and jobs, are available.

Does OpenRun need Nginx or Traefik in front of it?

No. OpenRun is implemented as a web server and does not depend on an external one like Nginx or Traefik, which is what allows it to scale idle apps down to zero and to do OAuth, SAML or certificate based auth with RBAC itself.

How does OpenRun handle SQLite data and per-app databases?

SQLite is managed with continuous Litestream replication to S3 and automatic restore. Service bindings provision isolated Postgres, MySQL, SQLite or Redis accounts for each app, with further databases available through binding providers.

Which command keeps OpenRun apps in step with Git?

Running openrun sync schedule sets up a background sync that creates new apps and updates existing apps as the config and code change in Git. The same objects can also be managed through the CLI or the browser console, and the three modes are described as mixable.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/openrundev-openrun.svg)](https://hysenlabs.com/projects/openrundev-openrun)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/openrundev-openrun.svg)](https://hysenlabs.com/projects/openrundev-openrun)

Was this useful?