Releases
How erxes/erxes versions are tagged, how Docker images and frontend bundles are published, and how to read a release as a self-hoster. For upgrading a deployment, see Upgrades.
Who cuts a release
Maintainers create releases from the shared integration branches; contributors never update release metadata. Pull requests are squash-merged and release notes are generated, not hand-written. The release itself is driven by release-it:
pnpm releaserunsrelease-it(declared as a dev dependency,^17.0.0)..release-it.jsondisables npm publishing, commits with the messagerelease ${version}(visible in history asrelease 3.1.9), creates a git tag named${version}matching the3.*pattern, and publishes a GitHub release namedv${version}.@release-it/conventional-changelogwith the Angular preset rewritesCHANGELOG.mdfrom conventional commit messages, which is whyfeat(scope):andfix(scope):matter. See Contribute to codebase.
The changelog
Each CHANGELOG.md entry links the compare range, groups commits under ### Bug Fixes and ### Features, and keeps issue and commit links. The 3.1.9 entry looks like this:
## [3.1.9](https://github.com/erxes/erxes/compare/3.1.8...3.1.9) (2026-09-17)
### Features
* **core:** add "Basic information" system fields to Settings → Properties (c8b7214)
* **core:** add member email activity log (#9350) (b65966c)
* **frontline:** use "Visible to create" for convert properties on every kind (f93184b)
What CI publishes
Per-area workflows under .github/workflows/ run builds on pull requests and publish artifacts only on pushes to main or version tags:
| Workflow files | Artifact | Tags |
|---|---|---|
ci-api-*.yml (gateway, core-api, 11 plugin APIs) | Docker image erxes/erxes-next-<service> (erxes-next-gateway, erxes-next-core-api, erxes-next-sales_api, …) | latest and YYYYMMDD-<short-sha> on main pushes (core-api also publishes from debug/core) |
ci-core-ui.yml | erxes/erxes-next-ui | latest, branch, short SHA, and semver tags via docker/metadata-action |
ci-service-*.yml, ci-saas-migrations.yml | erxes/erxes-next-logs, erxes/erxes-next-automations, erxes/erxes-next-migrations | latest and date-SHA |
ci-apps-*.yml | erxes/frontline-widgets, erxes/posclient-front, erxes/help-center | latest (help-center and frontline-widgets also get date-SHA or metadata tags) |
ci-ui-*.yml (plugin remotes) | No image; the Rspack Module Federation build is synced to Cloudflare R2 at s3://erxes-next/<folder>/<name>_ui/ | Folder is latest on main, or the tag name on a version tag |
The release workflow
Pushing a tag matching [0-9]* (or running the workflow manually with a version input) triggers release.yml. For each of the 19 images in its matrix (the gateway, core-api, all plugin APIs, logs, automations, erxes-next-ui, frontline-widgets, posclient-front, and help-center), it runs:
docker buildx imagetools create -t "<image>:<version>" "<image>:latest"
So a version tag does not rebuild anything; it promotes whatever :latest was current on Docker Hub at that moment. sentry-release.yml runs on the same tag and creates a Sentry release erxes-<version> on sentry.erxes.io, attaches commits, uploads source maps from frontend/core-ui/dist and frontend/plugins/*/dist, and marks a deployment.
Reading a release as a self-hoster
- Pin
erxes/erxes-next-<service>:3.1.9-style tags rather thanlatest. The version tag is a copy of the:latestbuild that was current at release time, so the tag andlatestcan diverge immediately afterward. - A product version does not prove every image has a matching tag; check the set you deploy with
docker buildx imagetools inspect erxes/erxes-next-core-api:3.1.9(ordocker manifest inspect). - Read
CHANGELOG.mdfor the fixes and features between your current version and the target, then check Database Migrations before following Upgrades. - Frontend plugin code is served from the versioned R2 folder, not from an image; a release updates both the images and the
erxes-next/<version>/bundle prefix.