Skip to content
Back to skills

Ch Migrate

ASecurity

Use when integrating ClickHouse migrations into a project, setting up ch-migrate, creating migration files, bootstrapping ClickHouse databases, or troubleshooting ClickHouse Alembic issues. Triggers on "ClickHouse migration", "ch-migrate", "Alembic ClickHouse", "bootstrap ClickHouse", "migration user", "EXCHANGE TABLES".

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
databasespythonbashsqldockerawsgitdatabase

Works with

  • cli
  • mcp

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 27, 2026

npx -y skills add David-Li0406/meta-skill-evloving --skill ch-migrate --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ch Migrate?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ch Migrate
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/david-li0406-ch-migrate/badge)](https://www.skillsdirectory.com/skills/david-li0406-ch-migrate)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ch-migrate
description: Use when integrating ClickHouse migrations into a project, setting up ch-migrate, creating migration files, bootstrapping ClickHouse databases, or troubleshooting ClickHouse Alembic issues. Triggers on "ClickHouse migration", "ch-migrate", "Alembic ClickHouse", "bootstrap ClickHouse", "migration user", "EXCHANGE TABLES".
---

# ch-migrate: ClickHouse Migration Tool

## Overview

`ch-migrate` is a CLI tool for managing ClickHouse schema migrations built on Alembic. It handles multi-environment configuration, role-based access control, and ClickHouse Cloud compatibility.

**Install:** `uv add clickhouse-alembic` or `pip install clickhouse-alembic`

## CLI Quick Reference

| Command | Description |
|---------|-------------|
| `ch-migrate init [PATH] [--name NAME]` | Initialize project structure |
| `ch-migrate bootstrap ENV [--dry-run]` | Create database, roles, users |
| `ch-migrate new ENV NAME [--table X]` | Create migration (optionally with SQL file) |
| `ch-migrate up ENV [-r REV]` | Apply migrations (default: head, or to REV) |
| `ch-migrate down ENV [-r REV]` | Rollback (default: last, or to REV) |
| `ch-migrate status ENV` | Show current migration state |
| `ch-migrate history ENV` | Show migration history |
| `ch-migrate skill [--user\|--project]` | Install Claude skill for ch-migrate |

**Options for `new`:** `--table NAME`, `--view NAME`, `--dict NAME` create SQL history files organized by object name in `migrations/sql/history/{tables|views|dictionaries}/{object_name}/`.

## Project Structure

```
project/
├── config.yaml           # ClickHouse hosts and settings
├── .env.local            # Secrets (gitignored)
├── alembic.ini           # Alembic configuration
└── migrations/
    ├── env.py            # Alembic environment
    ├── versions/         # Migration Python files
    └── sql/
        ├── bootstrap/    # Custom bootstrap SQL (optional)
        └── history/      # Object-centric SQL versions
            ├── tables/
            ├── views/
            └── dictionaries/
```

## Configuration

### config.yaml

```yaml
project:
  name: my_project

defaults:
  port: 8443                    # 8123 for local HTTP
  secure: true                  # false for local Docker
  admin_user: default
  # Optional users (uncomment to enable):
  # mcp_user_name: mcp_reader   # Read-only for AI tools
  # dict_reader_name: dict_reader

environments:
  dev:
    host: dev.clickhouse.cloud  # or localhost for Docker
    database: my_project_dev
    migration_user: migration_dev
    # aws_region: us-east-1  # Optional: for region-scoped SSM lookups
    # Optional SSM paths (if set, fetches from SSM directly):
    # Supports JSON key extraction: /path/to/param#json_key
    # ssm:
    #   admin_password: /my_project/dev/admin_password
    #   migration_password: /my_project/credentials#password

  staging:
    host: staging.clickhouse.cloud
    database: my_project_staging
    migration_user: migration_staging

  production:
    host: prod.clickhouse.cloud
    database: my_project
    migration_user: migration_prod
```

### .env.local

```bash
# Required
CH_DEV_MIGRATION_PASSWORD=your-migration-password
CH_DEV_ADMIN_PASSWORD=your-admin-password    # For bootstrap only

# Optional (if mcp_user_name configured)
CH_DEV_MCP_PASSWORD=your-mcp-password

# Repeat for staging/production with appropriate env name
CH_PRODUCTION_MIGRATION_PASSWORD=prod-password
CH_PRODUCTION_ADMIN_PASSWORD=prod-admin-password
```

## Integration Workflow

### For Existing Projects

```bash
# 1. Install
uv add clickhouse-alembic

# 2. Initialize (in project root or subdirectory)
ch-migrate init ./migrations --name my_project

# 3. Configure
# Edit migrations/config.yaml with your hosts
# Create migrations/.env.local with passwords

# 4. Bootstrap (safe on existing databases)
ch-migrate bootstrap dev --dry-run   # Preview
ch-migrate bootstrap dev             # Execute

# 5. Create baseline migration (documents existing schema)
ch-migrate new dev baseline

# 6. Apply baseline
ch-migrate up dev
```

### For New Projects (with Docker)

```bash
# Start ClickHouse
docker run -d --name clickhouse -p 8123:8123 \
  -e CLICKHOUSE_PASSWORD=admin123 clickhouse/clickhouse-server

# Initialize and configure for local (port 8123, secure: false)
ch-migrate init --name my_project
# Edit config.yaml: host: localhost, port: 8123, secure: false
# Create .env.local with CH_DEV_ADMIN_PASSWORD=admin123

ch-migrate bootstrap dev
ch-migrate new dev create_users_table --table users
ch-migrate up dev
```

## Migration Patterns

### Basic Table

```python
from alembic import op
from clickhouse_alembic import get_db

def upgrade():
    db = get_db()
    op.execute(f"""
        CREATE TABLE {db}.users (
            id UInt64,
            email String,
            created_at DateTime DEFAULT now()
        ) ENGINE = MergeTree() ORDER BY id
    """)

def downgrade():
    db = get_db()
    op.execute(f"DROP TABLE IF EXISTS {db}.users")
```

### Zero-Downtime with EXCHANGE TABLES

For schema changes that preserve data:

```python
def upgrade():
    db = get_db()

    # 1. Create new table with updated schema
    op.execute(f"""
        CREATE TABLE {db}.users_new (
            id UInt64,
            email String,
            phone String,  -- new column
            created_at DateTime DEFAULT now()
        ) ENGINE = MergeTree() ORDER BY id
    """)

    # 2. Copy data with transformation
    op.execute(f"""
        INSERT INTO {db}.users_new
        SELECT id, email, '' as phone, created_at FROM {db}.users
    """)

    # 3. Atomic swap
    op.execute(f"EXCHANGE TABLES {db}.users AND {db}.users_new")

    # 4. Drop old
    op.execute(f"DROP TABLE {db}.users_new")
```

### Using SQL Files

```python
from clickhouse_alembic import get_db, read_sql

def upgrade():
    db = get_db()
    # Reads from migrations/sql/history/tables/users/2026_01_08_1400_abc123.sql
    op.execute(read_sql("history/tables/users/2026_01_08_1400_abc123.sql", db=db))
```

## Roles Created by Bootstrap

| Role | Purpose |
|------|---------|
| `{project}_migration_role` | Schema changes (CREATE/DROP/ALTER TABLE/VIEW/DICTIONARY), data ops (SELECT/INSERT/DELETE/TRUNCATE), introspection |
| `{project}_readonly_role` | SELECT + SHOW (if mcp_user configured) |
| `{project}_dict_role` | Dictionary source access (if dict_reader configured) |

Note: Bootstrap uses explicit grants (not `GRANT ALL`) for ClickHouse Cloud compatibility.

## Troubleshooting

| Issue | Solution |
|-------|----------|
| "migration_user required" | Add `migration_user: username` to environment in config.yaml |
| "Connection refused" | Check host/port. Local Docker: port 8123, secure: false |
| Passwords not loading | Ensure .env.local exists in project root (not migrations/) |
| Bootstrap hangs | Verify admin_password is correct for admin_user |
| "Database does not exist" | Run `ch-migrate bootstrap ENV` first |

### Verify Configuration

```bash
# Check what SQL would run
ch-migrate bootstrap dev --dry-run

# Should show masked passwords like:
# CREATE USER IF NOT EXISTS migration_dev
# IDENTIFIED BY '********';
```

## ClickHouse Cloud Notes

- Uses standard engines (MergeTree, ReplacingMergeTree) - auto-upgraded to Shared* on Cloud
- Default port 8443 (HTTPS), use 8123 for local HTTP
- DDL is non-transactional - migrations can't be atomically rolled back
- Each `op.execute()` runs one statement

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…