PocketBase Migrations
Schema migrations and versioning for PocketBase. Use when creating migrations, managing schema versions, syncing collections between environments, using automigrate, or creating collections programmatically. Covers migrate commands, migration file format, snapshot imports, and the _migrations tracking table.
Best use case
PocketBase Migrations is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Schema migrations and versioning for PocketBase. Use when creating migrations, managing schema versions, syncing collections between environments, using automigrate, or creating collections programmatically. Covers migrate commands, migration file format, snapshot imports, and the _migrations tracking table.
Teams using PocketBase Migrations should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/pb-migrations/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How PocketBase Migrations Compares
| Feature / Agent | PocketBase Migrations | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Schema migrations and versioning for PocketBase. Use when creating migrations, managing schema versions, syncing collections between environments, using automigrate, or creating collections programmatically. Covers migrate commands, migration file format, snapshot imports, and the _migrations tracking table.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agents for Coding
Browse AI agent skills for coding, debugging, testing, refactoring, code review, and developer workflows across Claude, Cursor, and Codex.
Best AI Skills for Claude
Explore the best AI skills for Claude and Claude Code across coding, research, workflow automation, documentation, and agent operations.
ChatGPT vs Claude for Agent Skills
Compare ChatGPT and Claude for AI agent skills across coding, writing, research, and reusable workflow execution.
SKILL.md Source
# PocketBase Migrations & Schema Versioning
## Overview
PocketBase supports two approaches to schema management:
1. **Auto-migrate** (default in dev) — Dashboard changes auto-generate migration files in `pb_migrations/`
2. **Manual migrations** — write migration files by hand for full control
## CLI Commands
```bash
# Create a new empty migration file
./pocketbase migrate create "add_posts_collection"
# Creates: pb_migrations/1234567890_add_posts_collection.js
# Apply all pending migrations
./pocketbase migrate up
# Revert the last applied migration
./pocketbase migrate down
# Generate a full snapshot of all current collections
./pocketbase migrate collections
# Creates a migration file that recreates all collections from scratch
# Sync migration history with actual DB state (mark all as applied)
./pocketbase migrate history-sync
```
## Auto-migrate Mode
Enabled by default. When you change collections in the Dashboard, PocketBase auto-generates migration files in `pb_migrations/`.
```bash
# Start with auto-migrate (default)
./pocketbase serve
# Disable auto-migrate (production)
./pocketbase serve --automigrate=0
```
**Workflow**:
1. Develop with auto-migrate ON — use Dashboard to design schema
2. Migration files are auto-generated in `pb_migrations/`
3. Commit these files to git
4. Deploy: migrations run automatically on `serve` start
5. In production: use `--automigrate=0` to prevent Dashboard changes from generating new migrations
## Migration File Format
```js
// pb_migrations/1234567890_add_posts_collection.js
migrate(
// UP — apply migration
function(app) {
var collection = new Collection({
name: "posts",
type: "base",
fields: [
{ name: "title", type: "text", required: true },
{ name: "body", type: "editor" },
{ name: "author", type: "relation", collectionId: "USERS_COLLECTION_ID", cascadeDelete: false, maxSelect: 1, required: true },
{ name: "status", type: "select", values: ["draft", "published", "archived"] },
{ name: "published_at", type: "date" },
{ name: "tags", type: "relation", collectionId: "TAGS_COLLECTION_ID", maxSelect: 0 }
],
indexes: [
"CREATE INDEX idx_posts_author ON posts (author)",
"CREATE INDEX idx_posts_status ON posts (status)",
"CREATE UNIQUE INDEX idx_posts_title ON posts (title)"
],
listRule: "", // WARNING: "" means public access — use a filter or null to restrict
viewRule: "", // WARNING: "" means public access — use a filter or null to restrict
createRule: "@request.auth.id != ''",
updateRule: "author = @request.auth.id",
deleteRule: "author = @request.auth.id"
})
app.save(collection)
},
// DOWN — revert migration
function(app) {
var collection = app.findCollectionByNameOrId("posts")
app.delete(collection)
}
)
```
**Important**: the `app` inside migrations is a transactional instance. If any error occurs, the entire migration is rolled back.
## Creating Collections Programmatically
### Base collection
```js
var collection = new Collection({
name: "posts",
type: "base",
fields: [
{ name: "title", type: "text", required: true, min: 3, max: 200 },
{ name: "slug", type: "text", required: true, autogenerate: { pattern: "slugify(title)" } },
{ name: "body", type: "editor" },
{ name: "cover", type: "file", maxSelect: 1, maxSize: 5242880, mimeTypes: ["image/jpeg", "image/png", "image/webp"] },
{ name: "views", type: "number", min: 0 },
{ name: "metadata", type: "json", maxSize: 2000000 },
{ name: "featured", type: "bool" },
{ name: "published_at", type: "date" }
]
})
app.save(collection)
```
### Auth collection
```js
var collection = new Collection({
name: "users",
type: "auth",
fields: [
{ name: "name", type: "text", required: true },
{ name: "avatar", type: "file", maxSelect: 1, maxSize: 5242880 },
{ name: "role", type: "select", values: ["user", "editor", "admin"], required: true }
],
passwordAuth: { enabled: true, identityFields: ["email", "username"] },
oauth2: { enabled: true },
otp: { enabled: false },
mfa: { enabled: false },
authToken: { duration: 604800 } // 7 days
})
app.save(collection)
```
### View collection
```js
var collection = new Collection({
name: "posts_stats",
type: "view",
viewQuery: "SELECT p.id, p.title, COUNT(c.id) as comments_count, p.views FROM posts p LEFT JOIN comments c ON c.post = p.id GROUP BY p.id",
listRule: "",
viewRule: ""
})
app.save(collection)
```
## Modifying Existing Collections
```js
migrate(function(app) {
var collection = app.findCollectionByNameOrId("posts")
// Add a new field
collection.fields.add({
name: "subtitle",
type: "text",
max: 500
})
// Remove a field
collection.fields.removeByName("old_field")
// Update API rules
collection.listRule = "@request.auth.id != ''"
collection.viewRule = ""
// Add index
collection.indexes.push("CREATE INDEX idx_posts_subtitle ON posts (subtitle)")
app.save(collection)
}, function(app) {
var collection = app.findCollectionByNameOrId("posts")
collection.fields.removeByName("subtitle")
app.save(collection)
})
```
## Raw SQL in Migrations
```js
migrate(function(app) {
app.db().newQuery("ALTER TABLE posts ADD COLUMN legacy_id TEXT DEFAULT ''").execute()
app.db().newQuery("UPDATE posts SET legacy_id = id WHERE legacy_id = ''").execute()
}, function(app) {
app.db().newQuery("ALTER TABLE posts DROP COLUMN legacy_id").execute()
})
```
**Warning**: raw SQL bypasses PocketBase's schema cache. Run `migrate collections` afterward to re-sync if needed.
## Settings & Superuser in Migrations
### Initialize app settings
```js
onBootstrap(function(e) {
var settings = e.app.settings()
settings.meta.appName = "My App"
settings.meta.appURL = "https://myapp.com"
settings.meta.senderName = "My App"
settings.meta.senderAddress = "noreply@myapp.com"
settings.smtp.enabled = true
settings.smtp.host = "smtp.example.com"
settings.smtp.port = 587
settings.smtp.username = $os.getenv("SMTP_USER")
settings.smtp.password = $os.getenv("SMTP_PASS")
e.app.save(settings)
return e.next()
})
```
### Create superuser in migration
```js
migrate(function(app) {
var superusers = app.findCollectionByNameOrId("_superusers")
var record = new Record(superusers)
// IMPORTANT: always set PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD env vars
var email = $os.getenv("PB_ADMIN_EMAIL")
var password = $os.getenv("PB_ADMIN_PASSWORD")
if (!email || !password) {
throw new Error("PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD env vars are required")
}
record.set("email", email)
record.set("password", password)
app.save(record)
})
```
## Snapshot Migrations
`./pocketbase migrate collections` generates a complete snapshot — useful for:
- Bootstrapping a new environment
- Resetting migration history
- Reviewing full schema in one file
The generated file uses `app.importCollections(collections)` which supports two modes:
- **Default (merge/extend)**: adds new collections and fields, updates existing ones, doesn't delete anything
- **Delete missing**: `app.importCollections(collections, true)` — deletes collections/fields not in the snapshot
## `_migrations` Table
PocketBase tracks applied migrations in the internal `_migrations` table:
- `id` — auto-generated
- `file` — migration filename
- `applied` — timestamp
`migrate history-sync` marks all existing migration files as applied without running them — useful when importing an existing database.
## Best Practices
1. **Dev**: use auto-migrate + Dashboard for schema design, commit generated files
2. **Staging/Prod**: deploy with `--automigrate=0`, migrations run on startup
3. **Always write DOWN migrations** — reversibility saves you when things go wrong
4. **One concern per migration** — don't mix unrelated schema changes
5. **Test migrations**: apply on a copy of production data before deploying
6. **Use `migrate collections`** periodically to snapshot current state for documentation
7. **Never edit applied migrations** — create a new migration to fix issues
8. **Seed data**: prefer a dedicated migration for one-time initial data; if using `onBootstrap`, make the seed logic idempotent (existence checks/upserts) because bootstrap runs on every app startRelated Skills
PocketBase SDK
JavaScript SDK usage for PocketBase client applications. Use when calling PocketBase from frontend or Node.js, authenticating users, subscribing to realtime events, uploading files, or working with the PocketBase JS/TS SDK. Covers CRUD, auth flows, authStore, realtime SSE, file handling, batch operations, and query syntax.
PocketBase Hooks
Server-side JavaScript hooks for PocketBase (pb_hooks). Use when writing custom routes, event hooks, cron jobs, sending emails, making HTTP requests, querying the database, or extending PocketBase with server-side logic. Covers the goja ES5 runtime, routing, middleware, all event hooks, DB queries, record operations, and global APIs.
PocketBase Deploy
Production deployment for PocketBase. Use when deploying PocketBase to a server, setting up Docker, configuring systemd, reverse proxy (nginx/Caddy), TLS, SMTP, backups, S3 storage, rate limiting, or hardening for production. Provides ready-to-use configs.
PocketBase Collections
Collection and schema design for PocketBase. Use when creating collections, designing schemas, adding fields, setting up relations, or choosing between base/auth/view collection types. Prevents wrong field types, documents zero-default behavior, and covers relation cascading.
PocketBase API Rules
API rules and filter expressions for PocketBase access control. Use when setting permissions, writing filter expressions, configuring who can access what, or debugging 403/404 responses. Covers all 5 rule types, filter syntax, operators, request/collection macros, and field modifiers.
async-python-patterns
Comprehensive guidance for implementing asynchronous Python applications using asyncio, concurrent programming patterns, and async/await for building high-performance, non-blocking systems.
slack-automation
Automate Slack workspace operations including messaging, search, channel management, and reaction workflows through Composio's Slack toolkit.
linear-automation
Automate Linear tasks via Rube MCP (Composio): issues, projects, cycles, teams, labels. Always search tools first for current schemas.
jira-automation
Automate Jira tasks via Rube MCP (Composio): issues, projects, sprints, boards, comments, users. Always search tools first for current schemas.
gitops-workflow
Complete guide to implementing GitOps workflows with ArgoCD and Flux for automated Kubernetes deployments.
github-automation
Automate GitHub repositories, issues, pull requests, branches, CI/CD, and permissions via Rube MCP (Composio). Manage code workflows, review PRs, search code, and handle deployments programmatically.
github-actions-templates
Production-ready GitHub Actions workflow patterns for testing, building, and deploying applications.