Repository navigation
Choosing an Image
Two decisions: which image, and — if it includes Studio — which Studio hosting model.
| Image | Elsa API | Studio UI | Choose when |
|---|---|---|---|
runtime-*-server |
✅ | — | Studio is deployed separately, or you don't need a UI at all (headless workflow execution driven by API and triggers) |
runtime-*-studio |
— | ✅ | You already run an Elsa API and want a designer pointed at it |
runtime-*-combined |
✅ | ✅ | One container, one origin. Simplest to run and to secure |
Community images carry a ce- prefix and are public on GHCR —
ghcr.io/valence-works/runtime-ce-combined. Paid images drop the prefix and live in the private
registry — valenceruntimeimages.azurecr.io/runtime-combined. The bits are identical; see
Pulling Images.
Start with combined. One container, one port, one origin, no CORS. Most deployments never need
more than this.
Split into server + studio when you want to scale the API independently of the UI, expose the
API publicly while keeping Studio on an internal network, deploy them on different release cadences,
or run the UI in a DMZ. The cost is a CORS configuration and a second thing to operate.
Use server alone when workflows are triggered by API calls, HTTP endpoints or schedules and
nobody needs a designer in that environment — a common production shape where authoring happens in a
staging environment and workflow definitions are promoted.
Both the studio and combined images can run Studio either way. Set Studio__HostingModel.
The default is WebAssembly.
This choice determines who calls the Elsa API — and getting it wrong is the most common setup failure.
Studio runs in the browser. The browser calls the Elsa API directly.
Browser ──────────────► Elsa API
│ (downloads Studio)
└────► Studio host
- Set
Studio__Client__Backend__Url— note theClientsegment. This URL is served to the browser, so it must be reachable from the user's machine.http://elsa-server:8080is a Docker network name and will not resolve in a browser. - The server needs a CORS origin allowing the Studio origin:
Elsa__Cors__AllowedOrigins__0. - Only the
Studio:Clientconfiguration section is sent to the browser. Don't put secrets in it.
Pick this when you want browser-side interactivity, lower server load, and the API is reachable from wherever users are.
Studio runs on the server. The Studio container calls the Elsa API; the browser holds a SignalR connection to Studio.
Browser ──(SignalR)──► Studio host ──────► Elsa API
- Set
Backend__Url— noClientsegment. This is resolved inside the container, so a Docker service name likehttp://elsa-server:8080/elsa/apiis correct here. - No CORS configuration needed — the browser never calls the API directly.
- The API never has to be publicly reachable.
Pick this when the Elsa API is on a private network, or you want the API not exposed to browsers at all.
| WebAssembly | Blazor Server | |
|---|---|---|
| Config key | Studio__Client__Backend__Url |
Backend__Url |
| URL must resolve from | The user's browser | Inside the container |
| Docker service names work? | ❌ No | ✅ Yes |
| CORS needed on server? | ✅ Yes | ❌ No |
| API must be browser-reachable? | ✅ Yes | ❌ No |
| Latency sensitivity | Tolerant | Needs a stable connection |
| Server cost per user | Low | Higher (a live circuit per user) |
🔑 The rule:
Studio__Client__*is browser-facing. Everything else is container-facing. If Studio loads but shows connection errors, you almost certainly set a container-internal URL in the WebAssembly client setting. → Troubleshooting
Combined, WebAssembly (default) — nothing extra needed:
docker run -d -p 8080:8080 \
-e CShells__Shells__Default__Features__Identity__SigningKey="$(openssl rand -base64 32)" \
ghcr.io/valence-works/runtime-ce-combined:latestCombined, Blazor Server:
docker run -d -p 8080:8080 \
-e Studio__HostingModel=BlazorServer \
-e Backend__Url=http://localhost:8080/elsa/api \
-e CShells__Shells__Default__Features__Identity__SigningKey="$(openssl rand -base64 32)" \
ghcr.io/valence-works/runtime-ce-combined:latestSeparate, WebAssembly — note the browser-reachable URL and the matching CORS origin:
docker network create elsa
docker run -d --name elsa-server --network elsa -p 8080:8080 \
-e Elsa__Cors__AllowedOrigins__0=http://localhost:8081 \
-e CShells__Shells__Default__Features__Identity__SigningKey="$(openssl rand -base64 32)" \
ghcr.io/valence-works/runtime-ce-server:latest
docker run -d --name elsa-studio --network elsa -p 8081:8080 \
-e Studio__HostingModel=WebAssembly \
-e Studio__Client__Backend__Url=http://localhost:8080/elsa/api \
ghcr.io/valence-works/runtime-ce-studio:latestSeparate, Blazor Server — container-internal URL, no CORS:
docker run -d --name elsa-studio --network elsa -p 8081:8080 \
-e Studio__HostingModel=BlazorServer \
-e Backend__Url=http://elsa-server:8080/elsa/api \
ghcr.io/valence-works/runtime-ce-studio:latestEvery container listens on 8080 internally. The
8081:8080mapping is what puts Studio on a different host port.
Next: Pulling Images · Running the Images · Configuration
Valence Runtime — a product of Valence Works · Elsa Workflows is open source under the MIT License · Found an error in these docs? Open an issue.
Start here
Deploying
Going further
Commercial