HelloJohn / docs
hjctl CLI

hj migrations

Run and manage HelloJohn database migrations from the CLI — apply, rollback, status, and best practices for zero-downtime upgrades.

hj migrations

The hj migrations command group manages database schema migrations. Migrations must be run after updating HelloJohn to a new version and before starting the server.


Commands

CommandDescription
hj migrations statusShow current migration state
hj migrations runApply pending migrations
hj migrations rollbackRoll back the last migration batch
hj migrations listList all migrations and their status
hj migrations validateValidate database schema matches expected state

hj migrations status

Check the current migration state of your database:

hj migrations status
Database: postgresql://user:***@host:5432/hellojohn
Schema version: 20240115_001
Pending migrations: 2

  PENDING  20240601_001_add_passkey_support
  PENDING  20240601_002_session_device_fingerprint

If there are no pending migrations:

✅ Database schema is up to date (version: 20240601_002)

Flags:

FlagDescription
--database-url <url>Override HELLOJOHN_DATABASE_URL

hj migrations run

Apply all pending migrations:

hj migrations run
Running 2 pending migrations...

  ✅  20240601_001_add_passkey_support (234ms)
  ✅  20240601_002_session_device_fingerprint (89ms)

Migrations complete. Schema version: 20240601_002

Flags:

FlagDescription
--database-url <url>Override HELLOJOHN_DATABASE_URL
--dry-runShow what would be run without executing
--timeout <duration>Per-migration timeout (default: 5m)

Dry Run

Preview migrations without applying:

hj migrations run --dry-run
DRY RUN — No changes will be made

Would apply 2 migrations:
  20240601_001_add_passkey_support
  20240601_002_session_device_fingerprint

hj migrations rollback

Roll back the most recently applied batch of migrations:

hj migrations rollback
⚠️  Rolling back migration batch...
  ↩️  20240601_002_session_device_fingerprint (56ms)
  ↩️  20240601_001_add_passkey_support (120ms)

Rollback complete. Schema version: 20240115_001

Most HelloJohn migrations are forward-only. Rollback support is limited to migrations that include a down script. Check the changelog for rollback compatibility.

Flags:

FlagDescription
--steps <n>Number of batches to roll back (default: 1)
--dry-runPreview rollback without executing

hj migrations list

List all migrations with their status:

hj migrations list
VERSION                              STATUS     APPLIED AT
20240115_001_initial_schema          applied    2024-01-15 09:00:00
20240201_001_add_organizations       applied    2024-02-01 12:30:00
20240301_001_mfa_totp                applied    2024-03-01 15:45:00
20240401_001_passkeys                applied    2024-04-01 10:00:00
20240501_001_webhooks                applied    2024-05-01 11:22:00
20240601_001_add_passkey_support     pending    —
20240601_002_session_device_fp       pending    —

hj migrations validate

Validate that the database schema matches what HelloJohn expects:

hj migrations validate
✅ Schema validation passed
   All 34 tables present
   All indexes present
   All constraints valid

If validation fails:

❌ Schema validation failed

  Missing table: hj_passkey_credential
  Missing index: idx_hj_session_user_id_active

Run hj migrations run to apply missing migrations.


Deployment Workflow

Standard Deployment

The recommended workflow for upgrading HelloJohn:

# 1. Pull the new HelloJohn version
docker pull hellojohn/server:latest

# 2. Run migrations before starting new instances
docker run --rm \
  -e HELLOJOHN_DATABASE_URL="$DATABASE_URL" \
  hellojohn/server:latest \
  hj migrations run

# 3. Deploy new instances
docker compose up -d hellojohn

Zero-Downtime Migrations

HelloJohn migrations are designed to be backward-compatible — old instances can run against the new schema while new instances are rolling out:

  1. Run migrations (new schema, old code still works)
  2. Roll out new HelloJohn instances (blue/green or rolling)
  3. Old instances continue serving during rollout
  4. Once all instances are updated, the migration window closes

What this means: Migrations never remove columns or tables in the same release they add replacements. Old columns are removed in a subsequent release only after all instances have been updated.

Kubernetes Deployment

Use an init container to run migrations before the main container starts:

initContainers:
  - name: migrate
    image: hellojohn/server:v2.0.0
    command: ["hj", "migrations", "run"]
    env:
      - name: HELLOJOHN_DATABASE_URL
        valueFrom:
          secretKeyRef:
            name: hellojohn-secrets
            key: database-url

See Kubernetes Deployment for a full example.


Troubleshooting

Migration stuck or timed out

If a migration times out, check for long-running PostgreSQL locks:

SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes';

Cancel blocking queries:

SELECT pg_cancel_backend(pid);

Migration failed partway through

Migrations run in transactions. If a migration fails, changes are rolled back automatically. Fix the underlying issue and run hj migrations run again.

Database URL not configured

Error: HELLOJOHN_DATABASE_URL is not set

Pass it explicitly:

hj migrations run --database-url "postgresql://user:password@host:5432/hellojohn"

On this page