Skip to content

Temporal Admin Access ​

The Temporal Web UI is an administrative console for the workflow engine, not a product feature. Hegemony keeps it closed and a platform admin opens it deliberately, on the host, for as long as the investigation takes.

What is closed, and why ​

Two properties decide the whole design:

  • It authenticates nobody. The temporalio/ui image ships no login. Anything that can reach its port is an administrator.
  • It is scoped to no organization. The platform runs a single Temporal namespace and temporalio/auto-setup ships no authorizer plugin, so whoever reaches the console reads every organization's workflow inputs and outputs and can terminate, signal or reset their runs. There is no per-tenant view of it to hand out.

So the console is not a thing to put a role gate in front of — a role gate would hide the link, not the console. It is closed at the layers that actually stop traffic:

LayerPosture
ContainerBehind the temporal-ui Compose profile — a plain up never creates it, in dev, prod or demo
Host portNothing published by default; when opened, pinned to the literal 127.0.0.1 with no variable that can widen it
Rebootrestart: "no", so the Docker daemon never brings back a console someone left open
HTTPS overlayNot proxied. The UI and Keycloak vhosts front services that authenticate their own users; the console does not
ApplicationRun detail does not link to it, and the API returns no console URL

The Temporal server's own gRPC frontend (7233) and its database are internal to the Docker network in production, and loopback-only in dev.

Tier 0: tctl inside the container ​

The cheapest path, and the one to reach for first: it opens no socket at all and answers most "what is this workflow doing?" questions.

bash
# dev
task compose:dev:exec -- temporal tctl --address temporal:7233 --ns default workflow describe --workflow_id <workflow-id>

# prod
task compose:prod:exec -- temporal tctl --address temporal:7233 --ns default workflow describe --workflow_id <workflow-id>

# demo
task compose:demo:exec -- temporal tctl --address temporal:7233 --ns default workflow describe --workflow_id <workflow-id>

The explicit --address is not optional: the compose files leave BIND_ON_IP unset, so the frontend binds the container's network address rather than 127.0.0.1, which is where a bare tctl connects. temporal:7233 is the Compose service address — the same one both healthchecks use — and it resolves from inside the container. Do not write $(hostname -i) here: your own shell expands it before task runs, so tctl would be handed the host's address. Temporal Retention & History Limits carries the retention and dynamic-config recipes.

Get the workflow ID from the run: as a platform admin, open the run in Hegemony and use Show Temporal identifiers in the run header, which reveals the workflow ID and the Temporal run ID with a copy button.

Tier 1: the console, on demand ​

When the history graph is genuinely what you need:

Each stack has its own task, so there is no way to open the wrong console by forgetting an environment variable:

bash
task compose:temporal:ui:dev
task compose:temporal:ui:prod
task compose:temporal:ui:demo

The task runs the console in the foreground with --rm and publishes it on 127.0.0.1 only. Its lifetime is your terminal: Ctrl-C closes it and removes the container. Nothing survives a reboot, and the next task compose:*:down sweeps the profile as well.

It prints the address to use, reading TEMPORAL_UI_PORT from that stack's own env file — 8080 for dev and prod, 8088 for the demo, which has to dodge the demo UI on 8080. Substitute your own value below if you have overridden it.

On a remote host, forward the loopback port rather than widening the bind:

bash
ssh -N -L 8080:127.0.0.1:8080 <user>@<host>     # 8088 for the demo stack

Then open http://127.0.0.1:8080 in your own browser.

Close it when you are done. The console has no session, no audit trail of its own, and no tenant boundary, so an open one is a standing cross-tenant administrative door.

Never route the console through the https overlay or any other reverse proxy without putting authentication on that vhost. It previously had one, on 0.0.0.0, with none — which is the exposure this design removes.

What platform admins get in the product instead ​

  • Run detail → Show Temporal identifiers — the workflow ID and Temporal run ID for the run in front of you, ready to paste into tctl or the console's search box.
  • Home → platform health — Temporal reachability and task-queue depth, without touching the console at all. See Home.

Checking the posture ​

bash
# No console container, in any state
docker compose ... ps -a --services | grep -qx temporal-ui && echo "OPEN" || echo "closed"

# No Temporal port on the host
ss -ltn | grep -E ':(8080|8088|5445)\b' || echo "no temporal port published"

COMPOSE_PROFILES must stay unset in your .env files: Compose reads COMPOSE_* from --env-file, so a single COMPOSE_PROFILES=temporal-ui line would re-open the console on every up, permanently and with no other signal.

Note for air-gapped hosts ​

docker compose pull skips services whose profile is not enabled, so temporalio/ui is no longer pre-pulled with the rest of the stack. The first task compose:temporal:ui:* pulls it — pre-pull the image if the host has no registry access.

Released as open source under the AGPL-3.0-or-later license. Development is sponsored by Rexonix s.r.o.. Contact — [email protected].