Installer Bundle & Versioning
The CTMS platform is deployed to production servers using a self-contained install bundle — a single tarball that contains everything needed for a full deployment without requiring git, GitHub tokens, or network access to source repos.
What's in the Bundle
The bundle is assembled by a GitHub Actions workflow and packaged as zynctl-bundle-<version>.tar.gz. It includes:
| Component | Path in Bundle | Purpose |
|---|---|---|
| zynctl.sh | zynctl.sh | Platform controller — bootstrap, deploy, operate |
| Environment template | .env.example | Template for .env.production |
| Docker Compose | docker-compose.yml, docker-compose.prod.yml | Core service definitions |
| Caddy | caddy/ | Reverse proxy configs (dev + prod) |
| OTEL + Fluent Bit | config/otel/, config/fluent-bit/ | Observability pipeline configs |
| KrakenD | ctms-api-gateway/configs/ | API gateway template + entrypoint |
| Supabase | supabase/ | Full self-hosted Supabase stack (compose + volumes) |
| Frappe | frappe-marley-health/ | Frappe compose + env sample + setup scripts |
| Demo data | scripts/ | Seed data, patient loader, demo data JSON |
| VERSION | VERSION | Bundle version stamp |
| zynctl.conf.example | zynctl.conf.example | Optional configuration overrides |
Versioning Scheme
Bundle releases use a date-stamped version format:
bundle-v{MAJOR}.{MINOR}.{YYYYMMDD}
| Segment | Meaning | Example |
|---|---|---|
MAJOR | Platform generation (currently 2) | 2 |
MINOR | Sequential release number (increment by 1) | 35 |
YYYYMMDD | Release date | 20260315 |
Example: bundle-v2.35.20260315 → Platform gen 2, 35th release, built on 2026-03-15.
For non-tagged automatic builds (push to main), the version is auto-computed as v{MAJOR}.{run_number}.{YYYYMMDD} using the GitHub Actions run number.
Tagged Releases
For formal releases, create a git tag with the bundle-v prefix across all 4 repos:
TAG="bundle-v2.35.20260315"
MSG="fix: Marquez init, pipeline trigger semantics, env completeness"
# Tag all repos with the same version
for repo in ctms.devops ctms-web docs.zynomi.com ctms-data-pipeline-ai-analytics; do
cd /path/to/$repo && git tag "$TAG" -m "$MSG"
done
# Push all tags (ctms.devops triggers the bundle build)
for repo in ctms.devops ctms-web docs.zynomi.com ctms-data-pipeline-ai-analytics; do
cd /path/to/$repo && git push origin "$TAG"
done
Tagged builds will:
- Use the tag version instead of auto-computed version
- Create a GitHub Release with the tarball and SHA256 checksum attached
- Appear under ctms.devops Releases
Repos That Get Bundle Tags
| Repository | Why it gets tagged |
|---|---|
ctms.devops | Docker Compose, configs, env — triggers build |
ctms-web | Zynexa web app, API routes, OpenAPI spec |
docs.zynomi.com | Documentation site, synced OpenAPI spec |
ctms-data-pipeline-ai-analytics | dbt models, ingester, Cube.dev, MCP server |
Non-tagged Builds
Pushes to main that modify bundle-relevant files trigger an automatic build. These are uploaded as workflow artifacts (retained for 30 days) but do not create a GitHub Release.
How to Trigger a Bundle Build
There are three ways to trigger the installer bundle workflow:
1. Automatic (Push to main)
Any push to main that modifies these paths automatically triggers a build:
zynctl.sh— Platform controller.env.example— Environment templatedocker-compose.yml/docker-compose.prod.yml— Service definitionscaddy/**— Reverse proxy configsconfig/**— OTEL / Fluent Bit configsctms-api-gateway/configs/**— KrakenD gatewaysupabase/**— Supabase stackfrappe-marley-health/**— Frappe stackscripts/**— Demo data and setup scriptsscripts/frappe-setup/**— Frappe setup wizard (idempotent site configuration)
2. Manual Dispatch (GitHub UI)
- Go to Actions → Build and Publish Install Bundle
- Click "Run workflow"
- Select
mainbranch - Optionally enter a reason
- Click "Run workflow"
3. Manual Dispatch (CLI)
cd ctms.devops
# Trigger with default reason
gh workflow run build-install-bundle.yml
# Trigger with a custom reason
gh workflow run build-install-bundle.yml -f reason="Hotfix: refresh-token command added"
# Watch the run
gh run watch
4. Tagged Release
cd ctms.devops
# Create and push a version tag
git tag bundle-v2.5.20260303
git push origin bundle-v2.5.20260303
# This triggers the workflow AND creates a GitHub Release
Downloading a Bundle
From GitHub Releases (tagged builds)
# Download latest release
gh release download --repo zynomilabs/ctms.devops --pattern "zynctl-bundle-*.tar.gz"
# Download specific version
gh release download bundle-v2.5.20260303 --repo zynomilabs/ctms.devops
From Workflow Artifacts (non-tagged builds)
# List recent runs
gh run list --repo zynomilabs/ctms.devops --workflow build-install-bundle.yml
# Download artifact from a specific run
gh run download <run-id> --repo zynomilabs/ctms.devops
Deploying the Bundle
Once downloaded, deployment is straightforward:
# 1. Copy to server
scp zynctl-bundle-2.5.20260303.tar.gz root@<server-ip>:/root/
# 2. SSH in and extract
ssh root@<server-ip>
tar xzf zynctl-bundle-2.5.20260303.tar.gz
cd zynctl-bundle-2.5.20260303
# 3. Full deploy (bootstrap + deploy)
SERVER_HOST=<server-ip> ./zynctl.sh full-deploy
# 4. Or step-by-step
./zynctl.sh bootstrap # Install Docker, Compose, etc.
SERVER_HOST=<server-ip> ./zynctl.sh deploy # Deploy all stacks
After deployment, use zynctl.sh for day-to-day operations:
./zynctl.sh status # Show running containers
./zynctl.sh health # Hit all health endpoints
./zynctl.sh refresh-token # Fix Frappe API token after restart
./zynctl.sh logs [service] # View container logs
./zynctl.sh update # Pull latest images + restart
Workflow File
The bundle build is defined in:
ctms.devops/.github/workflows/build-install-bundle.yml
Key steps:
- Compute version — from tag or auto-generated
- Assemble bundle — copy files into bundle directory
- Create tarball —
tar.gz+ SHA256 checksum - Upload artifact — always (30-day retention)
- Create GitHub Release — only for tagged builds
Related
- CI/CD Build Pipelines — all images, triggers, and secrets
- CI/CD & Versioning — semver strategy and tag conventions
- Bundle Deployment — server-side deployment guide
- Docker Compose Profiles — profile-based service management
- Platform Runbook — operational reference