- Fill in the
.envfile with your desired database password, the url of your Windmill instance and your license key. - Run
docker-compose up -dto start the hub and database. - Update the instance settings in the Windmill instance to point to the hub.
- Optionally, you can restrict access to the hub by setting the
API_SECRETenvironment variable in the.envfile. Make sure to set it as well in the Windmill instance settings. - Optionally, you can set the
PUBLIC_APP_ACCESSIBLE_URLenvironment variable (APP_ACCESSIBLE_URLin the.envfile) to a different url than thePUBLIC_APP_URL(APP_URLin the.envfile) if you have a different url to access your windmill instance from the user's browser.
You can enable HTTPS handling by Caddy by adding the 443 port to the Caddy service in the docker-compose.yml file.
Authentication on the Hub is done through the Windmill instance. Both the Hub and the Windmill instances need to be running on the same domain for the authentication to work.
For instance, if the Windmill instance is available on windmill.example.com, the Hub should be accessible on a similar subdomain like hub.example.com. You will also need to set COOKIE_DOMAIN in the .env file of the Windmill instance (server container) to the root domain (e.g. example.com). After setting the COOKIE_DOMAIN, make sure to logout and login again.
- You can enable debug logs by setting
DEBUG_LOG=truein the environment section of the hub service in the docker-compose.yml file. - The hub exposes a debug page at
/debugthat displays the configuration and status of the hub. - If you need more support, please share with us the logs and and the debug page output.
As described above, logging in on the hub will redirect you to the Windmill instance you specified in the .env file.
Superadmins of the Windmill instance can approve resource types, scripts, flows, and apps on the Hub.
Only approved content will be available to users on the Windmill instance.
You can use the hub cli to pull scripts locally from the official Windmill hub and push them to your private hub.
Instead of approving scripts only from the hub UI, you can review them as GitHub
pull requests: a script created on the hub opens a PR in a content repo, and
merging the PR approves it on the hub. Example workflows are in
examples/github-workflows — copy them into the
.github/workflows of the git repo that holds your hub/ content (the repo you
pull/push with the hub cli). They are kept here as inert templates so they
don't run in this deployment repo.
create script in hub UI
│ hub fires repository_dispatch: hub-script-created
▼
open-pr-on-script-created.yaml ──► review PR (test-push-to-hub.yaml comments the impact)
│ reviewer merges
▼
push-to-hub.yaml ──► hub-cli push ──► script approved on the hub
# maintainer fast-track (no PR):
approve in hub UI ──► hub fires hub-script-approved ──► pull-on-script-approved.yaml ──► commit to main
Copy these into your content repo's .github/workflows/:
open-pr-on-script-created.yaml— triggered by the hub'shub-script-createddispatch. Runsmaterialize-askto write the new script's files and opens (or updates) a review PR on ahub-submission/script-<ask_id>branch. This is the real-time path from "created in the UI" to "reviewable PR".open-pending-prs.yaml— scheduled backstop (every 30 min). Runslist-pendingand opens a PR for any unapproved script the dispatch missed (GitHub outage, expired token, etc.). Uses the same branch names, so it never double-opens a PR the fast path already created.test-push-to-hub.yaml— runs on every PR touchinghub/**(including the submission PRs). A dry-runpushthat comments on the PR what merging will do on the hub. Pure preview — no hub changes.push-to-hub.yaml— runs on push tomaintouchinghub/**. Reconciles the repo into the hub viahub-cli push, which approves whatever is in the repo — so merging a submission PR approves that script. This closes the create → PR → merge → approved loop. (Omits--pruneby default; see the file's header for why under the "keep both approval paths" model.)pull-on-script-approved.yaml— triggered by the hub'shub-script-approveddispatch (fires only on UI approvals — the CLI push approves with?no_dispatch=true). Pulls the now-approved content into the repo and commits tomain, and closes the matching review PR if one was open. This is the event-driven replacement for a polling "update-from-hub" cron.
The two halves work together: open-* + push-to-hub cover the reviewed path
(created → PR → merge → approved), while pull-on-script-approved covers the
maintainer fast-track (approve in the UI → repo catches up). test-push-to-hub
gives reviewers a preview on every PR.
Set these on the hub service (in docker-compose.yml / .env) so it
dispatches events to your content repo (any hub can opt in — unset = disabled):
GITHUB_DISPATCH_REPO—owner/repoof your content repoGITHUB_DISPATCH_TOKEN— token allowed to create dispatch events (fine-grained PAT with Contents: write, or a GitHub App installation token)
In the GitHub repo holding your hub/ content, set:
secrets.HUB_TOKEN— a superadmin token of your Windmill instancesecrets.LICENSE_KEY— your enterprise license key (drop it for an unlicensed private hub)secrets.PR_TOKEN— a PAT/GitHub App token used to open the review PRs. Required so those PRs triggertest-push-to-hub.yaml(PRs opened with the defaultGITHUB_TOKENdon't trigger other workflows)vars.HUB_URL— your hub URL (e.g.https://hub.example.com)
Requires @windmill-labs/hub-cli recent enough to include the list-pending and
materialize-ask commands, and a hub that exposes latest_version on
/searchData?all=true.