CI/CD & Deployments
Promote workflow and app configurations between environments, automate deployments from your CI pipeline, manage environment variables, and roll back safely.
FlowOS supports configuration-as-code export and import, an environment promotion API, and pre-built CI/CD pipeline templates for GitHub Actions and GitLab CI. All deployment operations are logged in the audit trail.
Environments
Each workspace can have up to three named environments: development, staging, and production. Environments are independent execution contexts — each has its own connector credentials, secrets vault, and run history.
- •Workflows, apps, and SDK artifacts exist at the workspace level but are deployed to an environment.
- •An artifact can be active in staging but not yet promoted to production.
- •Environment-specific secrets (like production DB credentials) never leave their environment.
- •Each environment has its own API token and base URL subdomain.
deployer or owner role. If your workspace has deployment approvals enabled, a second user must approve before the deployment goes live.Promoting Between Environments
Via UI
- 1Open the artifact — navigate to the workflow, app, or SDK module you want to promote.
- 2Click Deploy — select the target environment from the dropdown.
- 3Review the diff — FlowOS shows a structured diff between the current active version and the version being deployed.
- 4Confirm — for production, you may be prompted for a deployment note and a second approver.
- 5Monitor — the Deploy Progress panel shows node-by-node activation status. Existing running instances continue on the old version until they complete.
Via Deploy API
/api/v1/deploymentsTrigger a deployment programmatically.
// Request body
{
"artifact_type": "workflow", // workflow | app | sdk_module | sdk_pack
"artifact_id": "wf_01HX...",
"version": 7, // null = deploy latest draft
"target_environment": "production",
"deployment_note": "Adds SLA breach escalation to on-call",
"notify_on_complete": ["usr_01HX..."]
}
// Response 202 Accepted
{
"deployment_id": "dep_01HX...",
"status": "queued",
"created_at": "2026-06-01T10:00:00Z"
}/api/v1/deployments/:idPoll deployment status.
/api/v1/deployments/:id/approveApprove a deployment pending approval.
/api/v1/deployments/:id/rejectReject a deployment.
/api/v1/deployments/:id/rollbackRoll back to the previous active version.
Configuration as Code
Export any workflow, app, or SDK artifact as a portable YAML bundle. Bundles include all node definitions, connector bindings (by slug, not credentials), and metadata.
/api/v1/export/workflow/:idExport a workflow definition as YAML.
/api/v1/import/workflowImport a workflow from a YAML bundle.
apiVersion: flowos/v1
kind: Workflow
metadata:
name: Incident Auto-Escalation
slug: incident-auto-escalation
description: Escalates P1 incidents to on-call if unacknowledged after 15 min
version: 7
tags: [itsm, escalation, p1]
trigger:
type: record_event
config:
table: incidents
events: [created, updated]
conditions:
- field: severity
op: eq
value: P1
nodes:
- id: wait-15m
type: Wait
config:
duration: 15
unit: minutes
- id: check-ack
type: Condition
config:
expression: "{{tables.incidents.status}} !== 'investigating'"
- id: page-oncall
type: Notify
config:
channel: pagerduty
connector: pagerduty-prod
template: p1-escalation
variables:
incidentId: "{{trigger.record.id}}"
edges:
- from: wait-15m
to: check-ack
- from: check-ack
to: page-oncall
condition: "true"GitHub Actions Pipeline
name: Deploy FlowOS Workflows
on:
push:
branches: [main]
paths: ['workflows/**']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install FlowOS CLI
run: npm install -g @flowos/cli
- name: Authenticate
run: flowos auth login --token ${{ secrets.FLOWOS_API_TOKEN }}
env:
FLOWOS_WORKSPACE: ${{ secrets.FLOWOS_WORKSPACE_SLUG }}
- name: Lint workflows
run: flowos validate workflows/
- name: Deploy to staging
run: flowos deploy workflows/ --env staging --wait
- name: Run smoke tests
run: flowos test run --env staging --suite smoke
- name: Promote to production
if: github.ref == 'refs/heads/main'
run: flowos deploy workflows/ --env production --wait --note "CD from ${{ github.sha }}"GitLab CI Pipeline
stages:
- validate
- deploy-staging
- test
- deploy-production
variables:
FLOWOS_WORKSPACE: $FLOWOS_WORKSPACE_SLUG
validate:
stage: validate
image: node:20
script:
- npm install -g @flowos/cli
- flowos auth login --token $FLOWOS_API_TOKEN
- flowos validate workflows/
deploy-staging:
stage: deploy-staging
script:
- npm install -g @flowos/cli
- flowos auth login --token $FLOWOS_API_TOKEN
- flowos deploy workflows/ --env staging --wait
smoke-tests:
stage: test
script:
- npm install -g @flowos/cli
- flowos auth login --token $FLOWOS_API_TOKEN
- flowos test run --env staging --suite smoke
deploy-production:
stage: deploy-production
only:
- main
script:
- npm install -g @flowos/cli
- flowos auth login --token $FLOWOS_API_TOKEN
- flowos deploy workflows/ --env production --wait
when: manual # require human approval in GitLabCLI Reference
# Authenticate
flowos auth login --token <token>
flowos auth status
# Validate (lint) all YAML files in a directory
flowos validate <path>
# Deploy a directory of YAML files to an environment
flowos deploy <path> --env <development|staging|production> [--wait] [--note "message"]
# Export a single artifact
flowos export workflow <id> --output ./workflows/
flowos export app <id> --output ./apps/
# Import an artifact
flowos import --file ./workflows/incident-auto-escalation.yaml --env staging
# List deployments
flowos deployments list --env production
# Rollback
flowos rollback --deployment <dep_id>
# Run a named test suite in an environment
flowos test run --env staging --suite smokeRollback
Every deployment stores the previously active version. Rolling back re-activates the previous version within seconds — no need to re-import or re-deploy the old YAML.
- •Rollback is instant for workflow definitions — running instances on the new version complete normally.
- •App rollbacks restart the preview container on the old bundle.
- •SDK module rollbacks take effect on the next invocation — no in-flight executions are interrupted.
- •A rollback is itself a deployment record and is logged in the audit trail.
--note "sha: abc1234") so you can cross-reference deployment history with your git log during incident postmortems.Database Migrations
Collection-level data migrations live in src/migrations/. Each script is a standalonetsx-executable file that reads MONGODB_URI from the environment, runs a safe transformation, and exits. Migrations are one-way and non-destructive by default — old collections are preserved so you can roll back by reverting the application code.
Available migrations
| Script | What it does | Run when |
|---|---|---|
| migrate-sys-global-to-workspace.ts | Copies records from the old global root_sys_counters and root_sys_notifications collections into per-workspace collections. Preserves max counter sequence values to avoid duplicate ticket numbers. | Once, before deploying the workspace-scoped sys_counters fix |
Running a migration
# From the flowos-backend root
npx tsx src/migrations/migrate-sys-global-to-workspace.tsThe script connects using MONGODB_URI from .env, logs a count of records found and copied per collection, and exits. It is safe to re-run — counter upserts use $max to never decrease a sequence value, and notification copies skip duplicates.
Writing a new migration
// src/migrations/my-migration.ts
import mongoose from 'mongoose'
import { config } from 'dotenv'
config()
const MONGO_URI = process.env['MONGODB_URI'] ?? 'mongodb://localhost:27017/flowos'
async function main() {
await mongoose.connect(MONGO_URI)
const db = mongoose.connection.db!
// --- do your work here ---
console.log('[migration] done')
await mongoose.disconnect()
}
main().catch(err => { console.error(err); process.exit(1) })