Migration Guide
This guide explains how to migrate between different versions of better-openclaw and how the config migration system works.
Understanding Config Versions
better-openclaw uses a versioned configuration schema. When the schema changes, a migration path ensures your existing configs continue to work.
Current Config Version: 2
Config files include a configVersion field:
{
"configVersion": 2,
"projectName": "my-project",
"services": ["redis", "postgresql"],
"deploymentType": "docker"
}
Automatic Migrations
Migrations run automatically when you load an older config:
import { migrateConfig, needsMigration } from "@better-openclaw/core";
const oldConfig = loadConfig("config.json");
if (needsMigration(oldConfig)) {
const migrated = migrateConfig(oldConfig);
// Config is now up to date
}
The CLI and API automatically apply migrations, so you typically don’t need to do this manually.
Migration History
v1 → v2: Add deploymentType field
What Changed: Added deploymentType field to distinguish between Docker and bare-metal deployments.
Before (v1):
{
"configVersion": 1,
"projectName": "my-project",
"services": ["redis", "postgresql"]
}
After (v2):
{
"configVersion": 2,
"projectName": "my-project",
"services": ["redis", "postgresql"],
"deploymentType": "docker"
}
Migration Logic:
// v1 → v2 migration
1: (input) => ({
...input,
configVersion: 2,
deploymentType: (input.deploymentType as string) ?? "docker"
})
The migration defaults deploymentType to "docker" to maintain backward compatibility. Existing Docker-based configs will continue to work without changes.
Manual Migration Steps
If you need to manually update a config file:
Identify Current Version
Check the configVersion field in your config file:cat config.json | grep configVersion
Update Config
Apply necessary changes to your config file:{
"configVersion": 2, // Update version number
"projectName": "my-project",
"services": ["redis", "postgresql"],
"deploymentType": "docker" // Add new required fields
}
Validate Config
Use the CLI to validate your updated config:pnpm --filter cli start
# Or use the API
curl -X POST http://localhost:3000/api/validate \
-H "Content-Type: application/json" \
-d @config.json
Breaking Changes by Version
Version 2 (Current)
Breaking Changes:
- Added required
deploymentType field (defaults to "docker")
- Native/bare-metal deployments now require explicit
deploymentType: "bare-metal"
Impact: Low — migration is automatic and defaults preserve existing behavior.
Action Required: None if using CLI or API. If manually constructing configs, add:
"deploymentType": "docker" // or "bare-metal" or "local"
Version 1 (Legacy)
Changes:
- Initial config schema
- No
deploymentType field
- All deployments assumed Docker
Action Required: Upgrade to v2 by loading config through the migration system.
Handling Future Migrations
When new versions are released:
- Check the Changelog: Review
CHANGELOG.md for migration notes
- Update Dependencies: Run
pnpm install to get the latest version
- Run Migrations: The system will automatically migrate on load
- Test Thoroughly: Regenerate your stack and verify all services work
Migration API
The migration system provides several utilities:
Check if Migration is Needed
import { needsMigration } from "@better-openclaw/core";
const config = { configVersion: 1, ... };
if (needsMigration(config)) {
console.log("Config needs migration");
}
Apply Migrations
import { migrateConfig, CURRENT_CONFIG_VERSION } from "@better-openclaw/core";
try {
const migrated = migrateConfig(oldConfig);
console.log(`Migrated to v${CURRENT_CONFIG_VERSION}`);
} catch (error) {
console.error("Migration failed:", error.message);
}
Get Current Version
import { CURRENT_CONFIG_VERSION } from "@better-openclaw/core";
console.log(`Current version: ${CURRENT_CONFIG_VERSION}`);
Migration Failure Handling
If a migration fails:
No Migration Path Error
Error: No migration path from config version X
Cause: Config is from a future version or invalid version number
Solution:
-
Check you’re using the latest version of better-openclaw:
git pull origin main
pnpm install
pnpm build
-
Verify config version is valid:
// Valid: version ≤ CURRENT_CONFIG_VERSION
configVersion: 2
// Invalid: future version
configVersion: 999
Validation Error After Migration
Error: Config fails validation after migration
Cause: Manual edits introduced invalid fields
Solution: Use the schema validator:
import { GenerationInputSchema } from "@better-openclaw/core";
try {
GenerationInputSchema.parse(config);
} catch (error) {
console.error("Validation errors:", error.issues);
}
Migrating Custom Services
If you’ve created custom service definitions:
When the ServiceDefinitionSchema changes, you must update your custom services to match the new schema.
Check Schema Changes
Review changes to packages/core/src/schema.ts in the release notes.
Update Service Definitions
Apply required changes to your custom services:export const myServiceDefinition: ServiceDefinition = {
// ... existing fields
// Add new required fields from latest schema
newField: "value"
};
Update Skills
If skill schema changed, update corresponding SKILL.md files.
Rollback Strategy
If you encounter issues after migration:
Option 1: Revert to Previous Version
git checkout v1.0.0 # Replace with previous stable version
pnpm install
pnpm build
Option 2: Use Backup Config
Always keep backups of working configs:
cp config.json config.backup.json
Restore if needed:
cp config.backup.json config.json
Option 3: Regenerate from Scratch
If config is corrupted, regenerate:
pnpm --filter cli start
# Select services and options interactively
Schema Reference
The latest schema is defined in packages/core/src/schema.ts. Key schemas:
GenerationInputSchema: Input config for generation
ServiceDefinitionSchema: Service definition format
SkillPackSchema: Skill pack format
ComposeOptionsSchema: Docker Compose generation options
All schemas use Zod for validation. You can introspect schemas programmatically:import { GenerationInputSchema } from "@better-openclaw/core";
// Get schema shape
console.log(GenerationInputSchema.shape);
// Validate and get errors
const result = GenerationInputSchema.safeParse(config);
if (!result.success) {
console.error(result.error.issues);
}
Testing Migrations
If you’re contributing migration code:
import { describe, it, expect } from "vitest";
import { migrateConfig } from "./migrations.js";
describe("Config Migrations", () => {
it("migrates v1 to v2", () => {
const v1Config = {
configVersion: 1,
projectName: "test",
services: ["redis"]
};
const migrated = migrateConfig(v1Config);
expect(migrated.configVersion).toBe(2);
expect(migrated.deploymentType).toBe("docker");
});
});
Run migration tests:
pnpm test packages/core/src/migrations.test.ts
Best Practices
- Always backup configs before upgrading versions
- Test migrations in a non-production environment first
- Review changelogs for breaking changes
- Keep dependencies up to date to avoid large migration jumps
- Validate after migration to catch issues early
- Never skip versions — migrations are sequential
- Don’t manually edit configVersion without applying corresponding changes
- Don’t assume forward compatibility — newer versions may not read older schemas
Getting Help
If you encounter migration issues:
- Check GitHub Issues for similar problems
- Review CHANGELOG.md for migration notes
- Ask in GitHub Discussions
- Open an issue with:
- Current config version
- Target config version
- Full error message
- Config file (redact secrets)