Repository navigation
Troubleshooting
docker logs elsa-pro # what actually went wrong
curl -f http://localhost:8080/health # is it up
docker ps -a # did it exit, and with what code
# what config actually landed
docker inspect elsa-pro --format '{{json .Config.Env}}' | tr ',' '\n' | grep -i cshells
⚠️ You cannotdocker execinto these images. They are chiselled: no shell, noprintenv, nocat, nocurl.docker exec elsa-pro printenvfails with executable file not found in $PATH — that is the image being minimal, not the container being broken. Everything below inspects from the host instead.
Most problems are one of three things: config landing somewhere other than where you think, a URL that resolves in the wrong network context, or a package that never loaded.
Environment variables beat the mounted file. A stray -e from an earlier experiment silently
overrides /config/config.json.
docker inspect elsa-pro --format '{{json .Config.Env}}' | tr ',' '\n' | grep -i cshellsPrecedence, later wins: appsettings.json → appsettings.{Environment}.json →
/config/config.json → environment variables. → Configuration
Also check the file is actually mounted where the app looks. docker cp reads out of the container
without needing anything inside it:
docker cp elsa-pro:/config/config.json - | tar -xOAn invalid JSON file may be skipped rather than fatal — so a stray trailing comma looks exactly like "my settings are ignored". Validate it:
python3 -m json.tool config.json > /dev/null && echo "valid"Note the shipped
config.example.jsoncontains//comments as documentation. Strip them before use if your tooling rejects them.
The most common cause: the shell key is wrong.
✅ CShells__Shells__Default__Features__DefaultAdminUser__AdminUsername
❌ CShells__Shells__0__Features__DefaultAdminUser__AdminUsername
Shells is keyed by shell name, not index. Using 0 doesn't error — it defines a different
shell called 0, and your admin user is provisioned into a shell you aren't using.
docker inspect elsa-pro --format '{{json .Config.Env}}' | tr ',' '\n' | grep DefaultAdminUserOther causes:
- Identity persistence isn't configured, so the user has nowhere to be stored. Both workflow and identity persistence must be set. → Persistence and Databases
- The user was created in an earlier database that has since been replaced. The admin is provisioned on first start against an empty store; changing the password in config afterwards does not update an existing user.
- Signing key changed, invalidating existing sessions. Sign in again.
Nearly always a hosting-model/URL mismatch. → Choosing an Image
WebAssembly (the default): the browser calls the API.
-
Studio__Client__Backend__Urlmust resolve 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 matching Studio's origin.
Blazor Server: the container calls the API.
-
Backend__Url(noClientsegment) must resolve inside the container — a Docker service name is correct here. - No CORS needed.
Diagnose from the browser's network tab: requests to a hostname that doesn't resolve, or CORS errors, tell you immediately which side is misconfigured.
# Reachable from the browser's perspective?
curl -f http://localhost:8080/elsa/api
# Reachable from inside the Studio container? There is no curl in the image, so borrow
# the Studio container's own network namespace with a throwaway one that has curl —
# same DNS, same network, so it answers the question the container would.
docker run --rm --network container:elsa-studio curlimages/curl:latest \
-f http://elsa-server:8080/elsa/apiAccess to fetch at 'http://localhost:8080/elsa/api' from origin
'http://localhost:8081' has been blocked by CORS policy
Add Studio's origin to the server:
-e Elsa__Cors__AllowedOrigins__0=http://localhost:8081The origin must match exactly — scheme, host and port. http://localhost:8081 and
http://127.0.0.1:8081 are different origins.
Not needed at all with Blazor Server or the combined image. If you're fighting CORS, consider whether you want one of those instead.
docker logs elsa-pro
docker ps -a # exit code| Symptom | Likely cause |
|---|---|
| Database connection errors | DB not ready or unreachable. Use depends_on: condition: service_healthy
|
| Migration failures | DB user lacks DDL permission, or a partially-migrated schema from a failed upgrade |
| Nuplane resolution errors | Feed unreachable, or a version range matching nothing → Extending with Nuplane |
| Port already allocated | Something else on 8080 — remap the host side |
| Exit 137 | OOM-killed. Raise the memory limit |
A package can load while its feature stays off. Work through, in order:
- Is the package in an
IncludePatternson a reachable feed? - Can the container reach the feed? Egress-filtered networks fail silently here.
- Does the version range match a package that exists?
- Is the feature name spelled exactly right?
- Is it under the shell you're running —
Default?
docker logs elsa-pro 2>&1 | grep -i nuplaneYou're on the default SQLite store, inside the container's writable layer. docker rm destroys it.
Move it to a volume, or better, use a real database. → Persistence and Databases
Multiple replicas without distributed coordination. Each instance independently fires schedules and processes triggers.
You need a shared database, a message bus, and distributed runtime features enabled — or scale back to one replica. → Extending with Nuplane · Production Checklist
In-memory scheduling doesn't survive restarts. Use durable Quartz scheduling backed by your database. → Extending with Nuplane
Set the externally reachable base URL:
CShells:Shells:Default:Features:Http:HttpActivityOptions:BaseUrl = https://elsa.example.com
This is what Elsa uses to build callback URLs handed to external systems, so it must be what a caller outside your network would use.
Error response from daemon: denied
- Logged in with your subscription token?
echo "$VALENCE_REGISTRY_PASSWORD" | docker login valenceruntimeimages.azurecr.io -u YOUR_TOKEN_NAME --password-stdin - Correct path?
valenceruntimeimages.azurecr.io/runtime-combined, not the Docker Hub name and not aghcr.ioname — those are the free Community images. - Correct tag?
runtime-studiois versioned on Elsa Studio, so its tag differs from the other two. - Subscription current? The token password carries an expiry aligned to your subscription period, so an expired subscription looks exactly like a wrong password.
⚠️ docker loginsucceeding does not mean you have access. The registry hands a token to any well-formed credential and only checks permissions when you pull. If you are diagnosing this, test adocker pull— a successful login proves nothing.
See Pulling Images for the full access model.
Include, or it'll be the first thing you're asked for:
- Image name and exact tag
-
docker logsoutput around the failure - Your
config.jsonwith secrets redacted - What you expected versus what happened
- Whether it reproduces without custom Nuplane packages
| Where | For |
|---|---|
| Issues | Image, packaging, deployment |
| Discussions | Questions |
| elsa-core issues | Engine bugs |
| Your private channel | Paid tiers — anything, with a response commitment |
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