Skip to content

Migrations

When you update FM CLI itself, run fm migrate to upgrade your benches and infrastructure to match the new version.

Overview

FM migrations operate at two levels:

  1. FM Infrastructure — Global services (MariaDB, Redis, nginx-proxy) and CLI configuration
  2. Benches — Individual bench environments, configurations, and compose files

Migrations are version-aware: FM tracks which version each component is migrated to and only applies necessary upgrade steps.

Quick Start

After updating FM:

# Update CLI first
fm self update

# Migrate infrastructure only (safe)
fm migrate

# Migrate specific bench
fm migrate mybench

# Migrate all benches
fm migrate --all-benches

Always update CLI before migrating

Run fm self update first, then fm migrate. Running migrations on an old CLI version can cause compatibility issues.


How Migration Works

Migration Process

  1. Pre-flight checks — Validates current state and determines required migrations
  2. Backup creation — Creates backups of config files + MariaDB dump
  3. Apply migrations — Executes version-specific upgrade steps
  4. Verification — Validates successful migration
  5. Cleanup — Updates version tracking, removes old backups (optional)

What Gets Backed Up

Component Backup Location Restoreable
Bench config files ~/frappe/sites/<bench>/backups/migrations/<timestamp>/ ✅ Yes
MariaDB database Same as bench config ✅ Yes (SQL dump)
Docker compose files Same as bench config ✅ Yes
Global config (fm_config.toml) ~/frappe/backups/migrations/<timestamp>/ ✅ Yes

Backup timestamp format

Backups use format: DD-Mon-YY--HH-MM-SS (e.g., 12-Apr-26--14-30-45)

Version Tracking

FM tracks migration state in two places:

Global infrastructure: ~/frappe/fm_config.toml

[migration_state]
system_migrated_to = "0.19.0"

Per-bench: ~/frappe/sites/<benchname>/bench_config.toml

[migration_state]
migrated_to = "0.19.0"
last_migration_date = "2026-04-12T14:30:45"


Migration Commands

Migrate Infrastructure Only

fm migrate

Applies only FM infrastructure migrations (global services, CLI config). Safe to run after every CLI update.

When to use:

  • After running fm self update
  • No benches need upgrading yet
  • Just want to update global services

Migrate Specific Bench

fm migrate mybench

Migrates a single bench to current FM version.

What happens:

  1. Backs up bench_config.toml, docker-compose*.yml, MariaDB database
  2. Applies bench-specific migration steps (SSL config format, compose changes, etc.)
  3. Updates migration_state.migrated_to in bench config

Bench must be stopped first

Stop the bench before migrating:

fm stop mybench
fm migrate mybench
fm start mybench


Migrate All Benches

fm migrate --all-benches

Migrates all benches in ~/frappe/sites/.

Confirmation prompt:

Found 5 benches requiring migration:
  - mybench (v0.18.0 → v0.19.0)
  - testbench (v0.18.0 → v0.19.0)
  - prod (v0.18.0 → v0.19.0)

Proceed with migration? [y/n]:

Skip prompt:

fm migrate --all-benches --auto-proceed

Exclude Benches

fm migrate --all-benches --exclude-bench oldbench,legacy

Migrate all benches except specified ones (comma-separated).

Use for problematic benches

Exclude broken or archived benches from mass migrations, then handle them individually later.


Failure Handling

On-Failure Strategies

Control what happens when a bench migration fails:

--on-failure=prompt (default)

Asks what to do after each failure.

fm migrate --all-benches --on-failure=prompt

Interactive prompt:

Migration failed for 'mybench':
  Error: SSL certificate format incompatible

[yes] Rollback bench to pre-migration state
[no] Skip rollback - Leave bench as-is (manual fix required)

Do you want to rollback the bench? [yes/no]:

--on-failure=rollback

Automatically rollback on failure (safest for single bench).

fm migrate mybench --on-failure=rollback

What happens:

  1. Migration fails
  2. Restores backups (config files, docker-compose files, MariaDB dump)
  3. Bench returns to pre-migration state
  4. Exit with error

Recommended for production benches

Use rollback for critical production benches to ensure they never end up in a half-migrated state.


--on-failure=archive

Move failed benches to archive, continue migrating others (partial success OK).

fm migrate --all-benches --on-failure=archive

What happens:

  1. Migration fails for mybench
  2. Moves ~/frappe/sites/mybench/~/frappe/archived-sites/mybench/
  3. Continues migrating remaining benches
  4. Prints summary of archived benches at end

Only for --all-benches

Archive mode requires --all-benches (not supported for single bench migrations).

Use case: Large bench fleets where some failures are acceptable, and you'll investigate archived benches later.


Backup Management

Skip All Backups (Dangerous)

fm migrate --all-benches --skip-all-backup

Use only in controlled scenarios

Skipping backups means no rollback possible if migration fails. Only use when:

  • You have external backups
  • Backup creation itself is failing (disk space, permissions)
  • Testing in disposable dev environments

Skip Backups for Specific Benches

fm migrate --all-benches --skip-backup-for testbench,devbench

Skip backups for specified benches (comma-separated), still back up others.

Use case: Dev benches with large databases where backup time is prohibitive.


Backup Restoration

Manual Rollback

If you need to manually restore a bench from backup:

# Find backup timestamp
ls ~/frappe/sites/mybench/backups/migrations/

# Example: 12-Apr-26--14-30-45
BACKUP_TS="12-Apr-26--14-30-45"

# Stop bench
fm stop mybench

# Restore config files
cp ~/frappe/sites/mybench/backups/migrations/$BACKUP_TS/bench_config.toml \
   ~/frappe/sites/mybench/

# Restore database (requires bench running)
fm start mybench
fm shell mybench -c "bench restore ~/workspace/frappe-bench/sites/backups/migrations/$BACKUP_TS/database.sql.gz"

Database backups are gzipped SQL dumps

Use bench restore command inside the bench to restore database from .sql.gz file.


Version-Specific Migration Notes

v0.19.0 Notable Changes

Toolchain changes:

  • Switched from pyenv + nvm to uv + fnm for Python/Node version management
  • Python/Node versions now auto-detected from app pyproject.toml

SSL configuration changes:

  • Multi-certificate support (individual certs per domain)
  • New ssl_certificates array format in bench_config.toml
  • Old single-certificate configs automatically converted

Migration impact:

  • Requires bench container rebuild (Python/Node toolchain replacement)
  • SSL config files regenerated in new format
  • Approximately 2-5 minutes per bench (depending on app count)

Troubleshooting

Migration Fails with Backup Error

Symptom: Error creating backup: Disk quota exceeded

Solution:

# Check disk space
df -h ~/frappe

# Clean old backups
rm -rf ~/frappe/backups/migrations/<old-timestamp>/

# Or skip backups (if safe)
fm migrate mybench --skip-all-backup

Bench Stuck in Half-Migrated State

Symptom: Migration failed, bench won't start, rollback wasn't triggered

Solution:

# Find most recent backup
ls ~/frappe/sites/mybench/backups/migrations/ | sort -r | head -1

# Manually restore (see "Manual Rollback" above)

Migration Says "Already Up to Date" But Bench Broken

Symptom: fm migrate mybench says already migrated, but bench config is wrong

Solution:

Reset migration state to force re-migration:

# Edit bench_config.toml
nano ~/frappe/sites/mybench/bench_config.toml

# Remove or change [migration_state] section:
[migration_state]
migrated_to = "0.18.0"  # Set to previous version

# Save and re-run migration
fm migrate mybench

Migration Internals

Migration Discovery

FM automatically discovers and applies migrations by comparing:

  1. Current FM CLI version (from fm --version)
  2. Infrastructure migration state (from fm_config.toml)
  3. Each bench migration state (from bench_config.toml)

Migration selection:

  • If CLI = 0.19.0, infrastructure = 0.18.0 → Apply 0.19.0 infrastructure migration
  • If CLI = 0.19.0, bench = 0.18.0 → Apply 0.19.0 bench migration
  • If CLI = 0.19.0, bench = 0.19.0 → Skip (already migrated)

Migration Execution Order

  1. Infrastructure migrations (always first)
  2. Global database schema upgrades
  3. Global service config changes
  4. CLI config format updates

  5. Bench migrations (one bench at a time)

  6. Bench config format changes
  7. Docker compose file updates
  8. SSL certificate format conversions
  9. Per-bench database migrations (if needed)

Sequential execution

Benches are migrated sequentially (not parallel) to avoid database lock conflicts and ensure proper error handling.

See also: Configuration reference, Architecture reference