Chapter 29: CI/CD Integration
When your OpenClaw deployment grows to serve a team or organization, you want the same discipline applied to your config changes as to your code: version control, code review, automated testing, and automated deployment. This chapter covers how to integrate OpenClaw into a CI/CD pipeline.
Why CI/CD for OpenClaw?
- Catch config errors before production: Validate JSON schema, required fields, and channel credentials
- Peer review for changes: All allowlist changes, new workspaces, and skill additions go through PR review
- Audit trail: Every config change is a git commit with a clear author and timestamp
- Rollback: Git revert is instant recovery from a bad config deploy
- Automated testing: Integration tests verify the agent responds correctly after each change
Repository Structure
openclaw-config/
โโโ openclaw.json # Main config (no secrets โ uses env vars)
โโโ .env.example # Template for required environment variables
โโโ tests/
โ โโโ smoke-test.js # Basic connectivity tests
โ โโโ integration/
โ โโโ workspaces.test.js
โโโ scripts/
โ โโโ validate.sh # Config validation script
โ โโโ deploy.sh # Deployment script
โโโ .github/
โโโ workflows/
โโโ validate.yml # Run on every PR
โโโ deploy.yml # Run on merge to main
Config Validation
Validate your config before deploying:
openclaw config validate --config openclaw.json
Exit codes:
0โ Config is valid1โ Schema validation errors (malformed JSON, missing required fields)2โ Logical errors (workspace references unknown agent, etc.)
Example script (scripts/validate.sh):
#!/bin/bash
set -e
echo "Validating OpenClaw config..."
openclaw config validate --config openclaw.json
echo "Checking for required environment variables..."
required_vars=(
ANTHROPIC_API_KEY
TELEGRAM_BOT_TOKEN
SLACK_BOT_TOKEN
)
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "ERROR: Required environment variable $var is not set"
exit 1
fi
done
echo "All checks passed."
GitHub Actions: Validation Workflow
# .github/workflows/validate.yml
name: Validate Config
on:
pull_request:
paths:
- 'openclaw.json'
- '.env.example'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install OpenClaw CLI
run: npm install -g openclaw
- name: Validate config schema
run: openclaw config validate --config openclaw.json
- name: Check .env.example is up to date
run: |
openclaw config env-vars --config openclaw.json > required-vars.txt
diff required-vars.txt .env.example || (echo "ERROR: .env.example is out of date" && exit 1)
GitHub Actions: Deploy Workflow
# .github/workflows/deploy.yml
name: Deploy OpenClaw Config
on:
push:
branches: [main]
paths:
- 'openclaw.json'
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Install OpenClaw CLI
run: npm install -g openclaw
- name: Validate config
run: openclaw config validate --config openclaw.json
- name: Deploy to production server
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
# Copy config to server
echo "$DEPLOY_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
scp -i /tmp/deploy_key openclaw.json $DEPLOY_USER@$DEPLOY_HOST:~/.openclaw/openclaw.json
# Reload gateway without downtime
ssh -i /tmp/deploy_key $DEPLOY_USER@$DEPLOY_HOST 'openclaw reload'
- name: Run smoke tests
env:
OPENCLAW_API_KEY: ${{ secrets.OPENCLAW_API_KEY }}
OPENCLAW_BASE_URL: ${{ secrets.OPENCLAW_BASE_URL }}
run: node tests/smoke-test.js
Smoke Tests
Verify the gateway is healthy and workspaces respond after each deploy:
// tests/smoke-test.js
const { OpenClaw } = require('@openclaw/api-client');
const client = new OpenClaw({
baseUrl: process.env.OPENCLAW_BASE_URL,
apiKey: process.env.OPENCLAW_API_KEY
});
async function run() {
console.log('Running smoke tests...');
// Health check
const health = await client.health();
if (health.status !== 'ok') {
throw new Error(`Health check failed: ${JSON.stringify(health)}`);
}
console.log('โ Health check passed');
// Test each workspace responds
const workspaces = ['public', 'dev-team', 'admins'];
for (const workspace of workspaces) {
const res = await client.messages.send({
workspace,
userId: 'smoke-test',
message: 'Say "OK" and nothing else.'
});
if (!res.response.includes('OK')) {
throw new Error(`Workspace ${workspace} did not respond correctly`);
}
console.log(`โ Workspace ${workspace} responded`);
}
console.log('\nAll smoke tests passed.');
}
run().catch(err => {
console.error('Smoke test failed:', err.message);
process.exit(1);
});
Zero-Downtime Reloads
The openclaw reload command reloads the config without disconnecting active sessions or channel connections:
openclaw reload
What reload does:
- Parses and validates the new config
- Applies allowlist changes immediately
- Updates workspace definitions for new sessions (existing sessions continue on old config until they expire)
- Adds newly enabled channels
- Does NOT restart existing channel connections (avoiding reconnect delays)
For changes that require a full restart (new agents, changed ports):
openclaw restart
Rollback
If a deploy causes issues:
# On the server โ revert to the previous config
git -C ~/.openclaw revert HEAD
openclaw reload
Or from your CI/CD pipeline:
# Revert the commit on GitHub and push โ the deploy workflow runs again
git revert <bad-commit-hash>
git push
Environment-Specific Configs
For teams with staging and production environments:
openclaw-config/
โโโ base.json # Shared base config
โโโ staging.json # Staging overrides
โโโ production.json # Production overrides
# Merge base + environment-specific config
openclaw config merge base.json staging.json > merged-staging.json
openclaw config validate merged-staging.json
Next: Chapter 30 โ Teams & Enterprise โ Deploying OpenClaw at scale for large organizations: SSO, RBAC, audit logging, and compliance.