Quick start: Cabinee Community Installer
Docker Compose service names, installer environment variables (
UNIBPM_DOMAIN, and similar), and Keycloak realm/client IDs in the current Community installer retain historicalunibpm-*names from the shared codebase. The product you install and operate is Cabinee.
Cabinee Community Installer
This guide describes a quick launch of Cabinee Community using an all-in-one Docker Compose distribution.
What is included
- PostgreSQL (including a database for Keycloak)
- Kafka
- Keycloak
- Cabinee backend (
unibpm) - Cabinee frontend (
unibpm-frontend) - Cabinee Engine (
unibpm-engine) - Nginx gateway for domains/HTTPS (in Edge mode)
- Certbot for Let’s Encrypt (only if TLS is enabled)
Prerequisites
Required
- Docker Engine 20+
- Docker Compose v2 (
docker compose version)
For Edge + TLS (Let’s Encrypt)
- A public VM IP
- Open ports:
80/tcp(required for Let’s Encrypt HTTP-01)443/tcp(for HTTPS)
- DNS A records pointing to the VM IP (see below)
Two launch modes
- Local — access via
localhostports - Edge — access via domains and (optionally) TLS through Let’s Encrypt
In Edge, Cabinee and Keycloak must have an external URL (otherwise OAuth2 redirects will not match). Camunda does not have to be exposed externally.
Quick start
git clone https://github.com/Reunico/unibpm-community-installer.git
cd unibpm-community-installer
cp .env.example .env
nano .env
chmod +x install.sh prepare.sh
./install.sh
Login credentials
login=admin
password=admin
Verification:
docker compose ps
docker compose logs -f unibpm
docker compose logs -f keycloak
docker compose logs -f unibpm-engine
Mode A — Local
Minimal .env settings
DEPLOY_MODE=local
FRONT_PUBLIC_PORT=8080
BACKEND_PUBLIC_PORT=8099
KEYCLOAK_PUBLIC_PORT=8082
CAMUNDA_PUBLIC_PORT=8081
URLs after installation
- Cabinee UI:
http://localhost:8080/ - Keycloak:
http://localhost:8082/keycloak/ - Camunda (webapp):
http://localhost:8081/
Mode B — Edge (VM/cloud with DNS)
1) Publication schemes: subdomain vs path
Edge supports two routing schemes, set with the EDGE_ROUTING_MODE variable.
Option 1 — subdomain (recommended)
Separate domains:
- UI:
https://<CABINEE_DOMAIN>/ - Keycloak:
https://<KEYCLOAK_DOMAIN><KEYCLOAK_PATH>/(usually/keycloak) - Camunda (optional):
https://<CAMUNDA_DOMAIN>/(only ifEXPOSE_CAMUNDA=true)
Option 2 — path (everything on one domain)
A single domain:
- UI:
https://<CABINEE_DOMAIN>/ - Keycloak:
https://<CABINEE_DOMAIN><KEYCLOAK_PATH>/ - Camunda (optional):
https://<CABINEE_DOMAIN><CAMUNDA_PATH>/
In both options,
KEYCLOAK_PATHandCAMUNDA_PATHmust start with/.
2) DNS (required)
Subdomain:
UNIBPM_DOMAIN(Cabinee UI domain; legacy installer variable name) → VM IPKEYCLOAK_DOMAIN→ VM IPCAMUNDA_DOMAIN→ VM IP (only ifEXPOSE_CAMUNDA=true)
Path:
UNIBPM_DOMAIN(Cabinee public domain; legacy installer variable name) → VM IP
DNS check:
dig +short <CABINEE_DOMAIN>
dig +short <KEYCLOAK_DOMAIN>
3) Firewall / Security Group
Open:
80/tcp(always, if Let’s Encrypt)443/tcp(if HTTPS)
Quick check (important for Let’s Encrypt):
curl -I http://<CABINEE_DOMAIN>/
curl -I http://<KEYCLOAK_DOMAIN>/
4) Example .env for Edge (subdomain + TLS)
DEPLOY_MODE=edge
# subdomain | path
EDGE_ROUTING_MODE=subdomain
UNIBPM_DOMAIN=cabinee.example.com
KEYCLOAK_DOMAIN=auth.example.com
# Expose Camunda via DNS? (optional)
EXPOSE_CAMUNDA=false
CAMUNDA_DOMAIN=camunda.example.com
# Public paths
KEYCLOAK_PATH=/keycloak
CAMUNDA_PATH=/camunda
# TLS (Let’s Encrypt HTTP-01)
ENABLE_TLS=true
LETSENCRYPT_EMAIL=admin@example.com
NGINX_HTTP_PORT=80
NGINX_HTTPS_PORT=443
# Extra origins for WebSocket/CORS (comma-separated)
EXTRA_ALLOWED_ORIGINS=
Launch:
./install.sh
URLs after installation:
- Cabinee:
https://<CABINEE_DOMAIN>/(orhttp://...ifENABLE_TLS=false) - Keycloak:
- subdomain:
https://<KEYCLOAK_DOMAIN>/keycloak/ - path:
https://<CABINEE_DOMAIN>/keycloak/
- subdomain:
- Camunda:
- if
EXPOSE_CAMUNDA=true:- subdomain:
https://<CAMUNDA_DOMAIN>/ - path:
https://<CABINEE_DOMAIN>/camunda/
- subdomain:
- if
EXPOSE_CAMUNDA=false: it runs inside the docker network; for access use theCAMUNDA_PUBLIC_PORTport (if it is open) or an SSH tunnel/port forward
- if
Security note: the backend is published on
BACKEND_PUBLIC_PORT(8099) by default. In Edge, usually close this port on the firewall if it is not needed from the outside.
How the installer works (briefly)
The main command is install.sh. It can be run repeatedly: data in volumes is not deleted.
Sequence:
- Loads
.env - Computes and exports:
PUBLIC_SCHEME(http/https)KEYCLOAK_EXTERNAL_URL(external Keycloak URL for the browser/redirects)
- Generates
generated/nginx/default.conffromnginx/conf/*.tpltemplates (depending on the mode) - Starts infrastructure:
postgres,kafka,keycloak - Runs
prepare.sh, which:- waits for Keycloak to become ready
- obtains an admin token
- extracts client secrets
- generates:
generated/unibpm/application.yamlgenerated/engine/application.yaml
- (optionally) updates client redirect/web origins in Keycloak (depends on the mode and flags)
- Starts
unibpm,unibpm-engine,unibpm-frontend - In Edge, starts
nginx - In Edge + TLS, issues Let’s Encrypt certificates and restarts
nginx
Important about Keycloak URLs: internal vs external
The stand uses several different Keycloak URLs at the same time, and they must not be mixed up:
KEYCLOAK_BASE_URL (internal) — how containers reach Keycloak inside the docker network.
- edge + subdomain:
https://auth.example.com/keycloak - edge + path:
https://cabinee.example.com/keycloak - local:
http://keycloak:8080/keycloak
- edge + subdomain:
KEYCLOAK_INTERNAL_URL (host) — how prepare.sh, which runs on the host, reaches Keycloak. Usually this is the published port:
http://localhost:8082/keycloak.KEYCLOAK_EXTERNAL_URL (public/frontchannel) — how Keycloak is seen by the browser and where OAuth2 redirect_uri values must go. This value is computed by
install.sh(fromDEPLOY_MODE,EDGE_ROUTING_MODE,ENABLE_TLS, domains, and paths), for example:- edge + subdomain:
https://auth.example.com/keycloak - edge + path:
https://cabinee.example.com/keycloak - local:
http://localhost:8082/keycloak
- edge + subdomain:
Why this matters:
- if a container (
unibpm/unibpm-engine) tries to reachhttp://localhost:8082/...→ you getConnection refused; - if the browser is redirected to an internal URL (
http://keycloak:8080/...) → OAuth2 breaks; - if the issuer (
issvalue in the token) does not matchissuer-uriin the applications → the applications start rejecting JWTs.
Officially: in Keycloak you can (and often should) set the public URL (frontend) and the internal/backchannel separately, especially behind a reverse proxy.
What is used where in this distribution
Cabinee (unibpm) generated generated/unibpm/application.yaml uses:
spring.security.oauth2.resourceserver.jwt.issuer-uri→ KEYCLOAK_EXTERNAL_URL (issvalidation),...jwk-set-uriand...token-uri→ KEYCLOAK_BASE_URL (obtaining keys/tokens via the internal address).
Engine (unibpm-engine) generated generated/engine/application.yaml uses:
authorization-uri→ KEYCLOAK_EXTERNAL_URL (browser login),token-uri/jwk-set-uri→ KEYCLOAK_BASE_URL (internal calls),redirect-uriis built fromCAMUNDA_BASE_URL.
This is a normal and common scheme: frontchannel goes through the public URL, backchannel uses the internal address.
WebSocket (/stomp), CORS, and UI embedding
Cabinee uses a WebSocket endpoint (by default /stomp).
To avoid browser errors (including Safari) and issues when working through DNS/TLS/iframe, ws.allowed-origins must include:
http://localhost:3001(dev)http://localhost:<BACKEND_PUBLIC_PORT>(local backend)https://<CABINEE_DOMAIN>(edge)- any additional portals/domains if the UI is embedded or proxied
The installer generates origins from .env; set additional values via EXTRA_ALLOWED_ORIGINS.
Common issues
1) Let’s Encrypt does not issue a certificate
Check:
- the domains actually resolve to the VM IP (
dig +short ...) - port 80 is open from the outside
- nothing else is listening on port 80 (Apache/old nginx)
Check:
curl -I http://<CABINEE_DOMAIN>/
curl -I http://<KEYCLOAK_DOMAIN>/
docker compose logs -f nginx
2) Keycloak error: Invalid parameter: redirect_uri
This means that the Keycloak client has redirectUris that do not match the actual login URL (domain/path/scheme).
Where to fix:
- Keycloak Admin Console → Realm
unibpm(legacy realm name; the product is Cabinee) - Clients:
unibpm-front(Cabinee UI; legacy client id)camunda-identity-service(Camunda SSO, if you expose Camunda externally)
What must be in Redirect URIs:
UI (unibpm-front)
- Edge:
https://<CABINEE_DOMAIN>/*(orhttp://.../*without TLS) - Local:
http://localhost:<FRONT_PUBLIC_PORT>/*
Camunda (camunda-identity-service) — only if EXPOSE_CAMUNDA=true
- Local:
http://localhost:<CAMUNDA_PUBLIC_PORT>/*http://localhost:<CAMUNDA_PUBLIC_PORT>/login/oauth2/code/keycloak
- Edge subdomain:
https://<CAMUNDA_DOMAIN>/*https://<CAMUNDA_DOMAIN>/login/oauth2/code/keycloakhttps://<CAMUNDA_DOMAIN><CAMUNDA_PATH>/login/oauth2/code/keycloak(if you use a path structure inside the application)
- Edge path:
https://<CABINEE_DOMAIN><CAMUNDA_PATH>/*https://<CABINEE_DOMAIN>/login/oauth2/code/keycloakhttps://<CABINEE_DOMAIN><CAMUNDA_PATH>/login/oauth2/code/keycloak
If you changed the domain/path/scheme from http to https — update .env and run:
./install.sh
How to regenerate configs correctly (and why prepare.sh cannot be run “as is”)
prepare.sh expects the KEYCLOAK_EXTERNAL_URL variable to already be computed (this is done by install.sh).
Correct order after changing .env:
- If you changed domains/paths/mode/HTTPS → run the full scenario:
./install.sh
- If you changed only application parameters (for example, CORS/WS origins) and did not touch domains/paths/mode:
./install.sh
# or (alternative) export KEYCLOAK_EXTERNAL_URL manually and run prepare.sh,
# but this is for experienced users.
Recommendation for a stable issuer behind a reverse proxy
To make Keycloak always issue tokens with the expected iss (and not depend on who called it and at which address),
in edge mode it is recommended to set the Keycloak hostname/URL (for example, via KC_HOSTNAME/hostname settings).
This is especially important with EDGE_ROUTING_MODE=path and when publishing through nginx.
Update and restart
Restart everything:
docker compose restart
Recreate configs after editing .env:
./prepare.sh
docker compose restart unibpm unibpm-engine
If you changed domains/paths/edge settings, it is more convenient to run the full scenario:
./install.sh
Stop/remove
Stop:
docker compose down
Stop and remove the Postgres volume (⚠️ this deletes data):
docker compose down -v