# Quick start: versioned migrations

Create, seal, apply, and verify one migration against a disposable SQLite database.

Source: https://docs.ptah.run/v0.8.1/start/quick-start-migrations/

import { Tabs, TabItem } from '@astrojs/starlight/components';

Start with an empty directory. You will write one reversible migration pair,
seal the directory with `ptah.sum`, apply the pending migration to SQLite, and
verify both the recorded revision and the live table.

This tutorial does not roll the migration back. Rollback is a separate
operational task with its own safety contract.

## What you need

- A `ptah` binary on your `PATH`. [Install Ptah](../install/) if necessary.
- A terminal and about seven minutes.

No database server, Docker, or Go toolchain is required.

## Step 1. Create your first migration pair

Migration filenames begin with an integer version. Use the fixed version below
so your output matches this page.

<Tabs syncKey="shell">
<TabItem label="Bash">

```bash
mkdir -p ptah-versioned/migrations
cd ptah-versioned
cat > migrations/1700000000_create_users.up.sql <<'SQL'
CREATE TABLE users (
    id    INTEGER PRIMARY KEY,
    email TEXT NOT NULL
);
SQL
cat > migrations/1700000000_create_users.down.sql <<'SQL'
DROP TABLE users;
SQL
```

</TabItem>
<TabItem label="PowerShell">

```powershell
New-Item -ItemType Directory ptah-versioned/migrations | Out-Null
Set-Location ptah-versioned
@'
CREATE TABLE users (
    id    INTEGER PRIMARY KEY,
    email TEXT NOT NULL
);
'@ | Set-Content migrations/1700000000_create_users.up.sql
@'
DROP TABLE users;
'@ | Set-Content migrations/1700000000_create_users.down.sql
```

</TabItem>
</Tabs>

The up file changes the database. The down file describes how to reverse that
same version.

## Step 2. Seal the directory

```console
ptah migrations hash --dir ./migrations
```

Expected output on standard output:

```text
Wrote ./migrations/ptah.sum
2 migration file(s) hashed
```

Commit `ptah.sum` with the migration files. Later commands refuse a migration
file whose bytes no longer match the seal.

## Step 3. Apply the migration

```console
ptah migrations up --db-url sqlite://app.db --migrations-dir ./migrations
```

Expected output includes, on standard output:

```text
Current version: 0
Total migrations: 1
Pending migrations: 1

✅ Migrations completed successfully!
Database is now at version: 1700000000
```

Progress records on standard error include timestamps and correlation IDs, so
this page does not copy those volatile fields.

## Step 4. Verify the recorded revision

```console
ptah migrations status --db-url sqlite://app.db --migrations-dir ./migrations
```

Expected output includes, on standard output:

```text
Current Version: 1700000000
Total Migrations: 1
Applied Migrations: 1
Pending Migrations: 0
Out-of-order Migrations: 0
Status: ✅ Database is up to date
```

This answer comes from Ptah's revision table inside `app.db`.

## Step 5. Verify the live schema

```console
ptah db read --db-url sqlite://app.db
```

Expected output includes, on standard output:

```text
CREATE TABLE "users" (
  "id" INTEGER PRIMARY KEY,
  "email" TEXT NOT NULL
);
```

The migration history and the database shape now agree.

## Step 6. Clean up

<Tabs syncKey="shell">
<TabItem label="Bash">

```bash
cd ..
rm -rf ptah-versioned
```

</TabItem>
<TabItem label="PowerShell">

```powershell
Set-Location ..
Remove-Item -Recurse -Force ptah-versioned
```

</TabItem>
</Tabs>

## Next steps

- [Roll back to an explicit revision](../../versioned/rollback/).
- [Validate integrity and preconditions](../../versioned/integrity-and-safety/)
  before a shared deployment.
- [Generate migration files](../../versioned/generate/) from a desired schema.
- [Gate migration work in CI](../../testing/ci/).
