Creating Modules
This guide covers how to create reusable modules for OrchStep — from defining the module structure and config schema to publishing and versioning.
Module Structure
Section titled “Module Structure”my-module/ orchstep-module.yml # Module metadata and configuration orchstep.yml # Module workflow (tasks and steps) README.md # Documentation (optional)Module Definition (orchstep-module.yml)
Section titled “Module Definition (orchstep-module.yml)”name: ci-cdversion: "1.2.0"description: "CI/CD pipeline module with build, test, and deploy tasks"author: "your-org"license: "MIT"
# Configuration schema: what the consumer must/can provideconfig: required: - name: registry_url type: string description: "Docker registry URL" - name: app_name type: string description: "Application name" optional: - name: replicas type: integer default: 2 description: "Number of deployment replicas" - name: timeout type: string default: "60s" description: "Deployment timeout"
# What this module makes availableexports: tasks: - build - test - deploy - rollback
# Module dependenciesdependencies: - name: docker-utils version: "^1.0.0" source: "github.com/orchstep-modules/docker-utils" optional: falseModule Workflow (orchstep.yml)
Section titled “Module Workflow (orchstep.yml)”name: ci-cd-moduledesc: "CI/CD pipeline module"
vars: registry_url: "" app_name: "" replicas: 2 timeout: "60s"
tasks: build: desc: "Build Docker image" steps: - name: docker_build func: shell do: | docker build -t {{ vars.registry_url }}/{{ vars.app_name }}:{{ vars.version }} . outputs: image_tag: "{{ vars.registry_url }}/{{ vars.app_name }}:{{ vars.version }}"
test: desc: "Run test suite" steps: - name: unit_tests func: shell do: npm test - name: verify func: assert args: condition: '{{ eq steps.unit_tests.exit_code 0 }}' message: "Tests must pass"
deploy: desc: "Deploy to target environment" steps: - name: push_image func: shell do: docker push {{ vars.registry_url }}/{{ vars.app_name }}:{{ vars.version }}
- name: apply_manifest func: shell do: | kubectl set image deployment/{{ vars.app_name }} \ {{ vars.app_name }}={{ vars.registry_url }}/{{ vars.app_name }}:{{ vars.version }} kubectl rollout status deployment/{{ vars.app_name }} --timeout={{ vars.timeout }} retry: max_attempts: 3 interval: 10s
- name: health_check func: http args: url: "https://{{ vars.app_name }}.example.com/health" method: GET retry: max_attempts: 5 interval: 10s
- name: verify_health func: assert args: condition: '{{ eq steps.health_check.status_code 200 }}' message: "Health check must pass after deployment"
rollback: desc: "Rollback to previous version" steps: - name: undo func: shell do: kubectl rollout undo deployment/{{ vars.app_name }} - name: wait func: wait args: duration: 10s - name: verify func: http args: url: "https://{{ vars.app_name }}.example.com/health" method: GETConfig Schema Best Practices
Section titled “Config Schema Best Practices”Provide Sensible Defaults
Section titled “Provide Sensible Defaults”config: optional: - name: replicas type: integer default: 2 - name: log_level type: string default: "info" enum: ["debug", "info", "warn", "error"]Validate Required Inputs
Section titled “Validate Required Inputs”config: required: - name: registry_url type: string pattern: "^https?://" description: "Docker registry URL (must include protocol)"Document Everything
Section titled “Document Everything”config: required: - name: app_name type: string description: | Application name used for: - Docker image naming - Kubernetes deployment selector - Health check URL routingModule Patterns
Section titled “Module Patterns”Infrastructure Module
Section titled “Infrastructure Module”name: aws-ecs-deployconfig: required: - name: cluster_name type: string - name: service_name type: string - name: task_definition type: string optional: - name: desired_count type: integer default: 2exports: tasks: [deploy, rollback, scale, status]Notification Module
Section titled “Notification Module”name: slack-notifyconfig: required: - name: webhook_url type: string description: "Slack webhook URL" optional: - name: channel type: string default: "#deployments" - name: username type: string default: "OrchStep Bot"exports: tasks: [notify_success, notify_failure, notify_custom]Testing Module
Section titled “Testing Module”name: test-suiteconfig: optional: - name: coverage_threshold type: integer default: 80 - name: test_command type: string default: "npm test"exports: tasks: [unit, integration, e2e, coverage_report]Versioning
Section titled “Versioning”Modules use semantic versioning (semver):
| Version Constraint | Meaning |
|---|---|
1.2.3 | Exact version |
^1.2.0 | Compatible with 1.x.x (major locked) |
~1.2.0 | Compatible with 1.2.x (minor locked) |
>=1.2.0 | Minimum version |
Rules:
- Bump PATCH for bug fixes (1.2.3 -> 1.2.4)
- Bump MINOR for new features, backward-compatible (1.2.3 -> 1.3.0)
- Bump MAJOR for breaking changes (1.2.3 -> 2.0.0)
Publishing Checklist
Section titled “Publishing Checklist”- Create
orchstep-module.ymlwith complete metadata - Define clear config schema with descriptions
- Export only the tasks users need (keep internal tasks private)
- Tag releases with semver:
git tag v1.0.0 && git push --tags - Write a README with usage examples
- Validate before publishing:
orchstep module validate . - Test with
orchstep lintto ensure YAML validity
Anti-Patterns
Section titled “Anti-Patterns”- Don’t hardcode environment-specific values — use config parameters
- Don’t export internal helper tasks — only export the public API
- Don’t break backward compatibility in minor/patch releases
- Don’t require secrets in config — instruct users to use environment variables
- Don’t nest module dependencies more than 2 levels deep