Docker Deployment
The workflow engine is distributed as a Docker image and can be deployed standalone or as part of the full neops stack.
Docker Image
The image also carries the Monitor App sources together with a generated engine API client (built in the Dockerfile’s client-gen stage), so the app can be served directly from the image — neops-lab runs it this way with npm install && npm run dev inside /app/rest/monitor-app.
Tags follow the branch/release naming:
| Tag | Source |
|---|---|
latest |
Latest stable release |
develop |
Latest development build |
main |
Latest main branch build |
x.y.z |
Specific release version |
Docker Compose
The repository includes a ready-to-use docker-compose.yml that starts the development dependency stack (PostgreSQL + Monitor App):
make gen-fetch-client # the Monitor App image builds from the generated API client
docker compose up -d
Run the engine locally with npm run start:dev (see Setup). The base compose file only starts supporting services — the engine is not included.
| Service | URL | Purpose |
|---|---|---|
| PostgreSQL | localhost:5434 |
Database (mapped from container port 5432) |
| Monitor App | http://localhost:5173 | Web UI for workflow management |
Configuration
All settings can be overridden via environment variables or a .env file:
| Variable | Default | Description |
|---|---|---|
POSTGRES_PASSWORD |
unsafe |
Database password |
POSTGRES_PORT |
5434 |
Host port for PostgreSQL |
MONITOR_PORT |
5173 |
Host port for the Monitor App |
CMS dependency
Workflows that use acquire clauses or operate on specific entities (devices, interfaces,
groups) require a running CMS. Set NEOPS_CMS_URL to point to an existing instance — as a
host environment variable when running the engine locally via npm, or as a service
environment variable when you run the engine as a container of your own.
Building the Image
The Makefile helper builds either flavor with the right build args:
make build-docker # enterprise -> neops-workflow-engine:latest
make build-docker flavor=preview # developer-preview -> neops-workflow-engine-preview:latest
NPM_TOKEN is expected to already be exported in your environment; the target aborts with a clear
message if it is not. It runs the run-ci stage first, then builds the image itself.
To build by hand instead:
export NPM_TOKEN=ghp_xxx
DOCKER_BUILDKIT=1 docker build \
--secret id=npm_token,env=NPM_TOKEN \
--build-arg LICENSE_FLAVOR=enterprise \
--build-arg NEOPS_VERSION=1.4.0 \
-t quay.io/zebbra/neops-workflow-engine:latest .
The build uses BuildKit secrets so the npm token is never written to image layers.
Build-time variables
These are properties of the image, fixed when it is built. They are not runtime settings – see Configuration for the variables you set when deploying.
| Build arg | Required | Purpose |
|---|---|---|
LICENSE_FLAVOR |
Yes | enterprise or developer-preview. Selects the license compiled into the image |
NEOPS_VERSION |
No (develop) |
Version string the image reports at GET /version/ |
LICENSE_FLAVOR has no default
Omitting it fails the build. This is deliberate: defaulting would risk shipping the enterprise flavor unintentionally.
The chosen flavor is both compiled in and stamped into the image’s NEOPS_LICENSE_FLAVOR
environment variable. On startup the engine compares the two and refuses to boot if they
disagree, treating the image as corrupt. Do not override NEOPS_LICENSE_FLAVOR when running a
container – it is an integrity check, not a switch.
Running from source
When you run the engine from source rather than from an image (npm run start:dev), there
is no compiled flavor. NEOPS_LICENSE=developer-preview selects that flavor for local
development and tests; anything else yields enterprise. This variable has no effect on a
built image.
Authorization
The image runs NODE_ENV=production, so logs are JSON, the Swagger UI is not mounted at /api,
and every gate that opens only in a declared development environment stays closed.
| Variable | What a deployment sets it to |
|---|---|
NEOPS_AUTHZ_MODE |
enforce (the default), permissive, or disabled |
NEOPS_JWT_PUBLIC_KEY / NEOPS_JWT_PUBLIC_KEY_PATH / NEOPS_JWKS_URL |
The CMS’s RS256 verification key, inline, as a file path, or as a key-set URL. One of the three is required outside disabled |
NEOPS_CMS_TOKEN |
The token the engine calls the CMS with |
NEOPS_CORS_ORIGINS |
The exact browser origins allowed to call the API, comma-separated. Unset serves same-origin callers only |
The engine refuses to start, names the variable to set, and exits non-zero when:
- no verification key is configured and
NEOPS_AUTHZ_MODEis notdisabled NEOPS_AUTHZ_MODE=disabledis set withoutNEOPS_ALLOW_DISABLED_AUTHZ=trueNEOPS_CMS_TOKENis still the built-in placeholderunsafeNEOPS_JWKS_URLis plaintext HTTP without bothNODE_ENV=developmentandNEOPS_ALLOW_INSECURE_JWKS=true
See Configuration for the full list, the token claims the engine reads, and the permission strings each route requires.
Running Migrations
The engine runs pending migrations automatically on startup.
Networking
The engine needs to reach:
- PostgreSQL – for state persistence
- CMS – for entity data and locking (GraphQL over HTTP)
Workers need to reach the engine’s REST API (port 3030) for:
- Worker registration and heartbeat
- Function block registration
- Job polling and result pushing
The Monitor App runs client-side in the browser and connects to the engine API directly (default: http://localhost:3030, configurable in the Monitor App’s Settings page).
Health Check
The engine exposes a health endpoint:
For custom deployments that run the engine as a compose service, configure a health check like this:
services:
engine:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3030/health/"]
interval: 15s
timeout: 5s
retries: 5
start_period: 30s
See also: Configuration Reference for all environment variables, Setup for local development.

