{
  "topic": "cli.migrate",
  "path": [
    "cli",
    "migrate"
  ],
  "title": "cyoda migrate — run storage migrations",
  "synopsis": "`cyoda migrate` is a short-lived process that applies pending schema migrations for the configured storage backend, then exits cleanly — no admin listener, no background loops, no lingering goroutines.",
  "body": "# cli.migrate\n\n## NAME\n\ncli.migrate — run schema migrations for the configured storage backend and exit.\n\n## SYNOPSIS\n\n`cyoda migrate [--timeout <duration>]`\n\n## DESCRIPTION\n\n`cyoda migrate` is a short-lived process that applies pending schema migrations for the configured storage backend, then exits cleanly — no admin listener, no background loops, no lingering goroutines.\n\nIt loads the same configuration the server does via `app.DefaultConfig`, honoring all `CYODA_*` environment variables and `_FILE` suffix resolution identically to the main server process.\n\nDispatch is on `CYODA_STORAGE_BACKEND`:\n\n- **memory** — No-op; exits 0. The memory backend has no schema to migrate.\n- **sqlite** — No-op; exits 0. SQLite applies migrations lazily on first open; the migrate subcommand is not needed.\n- **postgres** — Runs the postgres plugin's embedded migration logic against `CYODA_POSTGRES_URL`. Exits 0 on success, 1 on error.\n- **other** — Exits 1 with \"unknown storage backend\".\n\nThe migrate subcommand respects the schema-compatibility contract: it refuses to run if the database schema is newer than the code's embedded maximum version. This prevents a rollback from accidentally downgrading a schema.\n\nThe primary consumer is the Helm chart's pre-install and pre-upgrade Job, which runs `cyoda migrate` before starting the server to ensure the schema is up to date.\n\n## OPTIONS\n\n- `--timeout <duration>` — Maximum duration for the migration run (default: 5 minutes). Accepts Go duration strings: `30s`, `2m`, `10m`, etc.\n\n## ENVIRONMENT VARIABLES\n\n- `CYODA_STORAGE_BACKEND` — Selects the backend to migrate (default: `memory`).\n- `CYODA_POSTGRES_URL` — PostgreSQL DSN, required when backend is `postgres`. Accepts `CYODA_POSTGRES_URL_FILE` variant.\n\n## EXIT CODES\n\n- `0` — Migration succeeded (or was a no-op for memory/sqlite).\n- `1` — Runtime error: bad config, database unreachable, migration failure, or timeout.\n- `2` — Flag-parse error.\n\n## EXAMPLES\n\n```\n# Migrate postgres schema (reads CYODA_POSTGRES_URL from environment)\nCYODA_STORAGE_BACKEND=postgres \\\n  CYODA_POSTGRES_URL=\"postgres://user:pass@localhost/cyoda\" \\\n  cyoda migrate\n\n# Migrate with a custom timeout\nCYODA_STORAGE_BACKEND=postgres \\\n  CYODA_POSTGRES_URL=\"postgres://user:pass@localhost/cyoda\" \\\n  cyoda migrate --timeout 2m\n\n# No-op — memory backend\ncyoda migrate\n```\n\n## SEE ALSO\n\n- config\n- cli.init\n",
  "sections": [
    {
      "name": "NAME",
      "body": "cli.migrate — run schema migrations for the configured storage backend and exit."
    },
    {
      "name": "SYNOPSIS",
      "body": "`cyoda migrate [--timeout <duration>]`"
    },
    {
      "name": "DESCRIPTION",
      "body": "`cyoda migrate` is a short-lived process that applies pending schema migrations for the configured storage backend, then exits cleanly — no admin listener, no background loops, no lingering goroutines.\n\nIt loads the same configuration the server does via `app.DefaultConfig`, honoring all `CYODA_*` environment variables and `_FILE` suffix resolution identically to the main server process.\n\nDispatch is on `CYODA_STORAGE_BACKEND`:\n\n- **memory** — No-op; exits 0. The memory backend has no schema to migrate.\n- **sqlite** — No-op; exits 0. SQLite applies migrations lazily on first open; the migrate subcommand is not needed.\n- **postgres** — Runs the postgres plugin's embedded migration logic against `CYODA_POSTGRES_URL`. Exits 0 on success, 1 on error.\n- **other** — Exits 1 with \"unknown storage backend\".\n\nThe migrate subcommand respects the schema-compatibility contract: it refuses to run if the database schema is newer than the code's embedded maximum version. This prevents a rollback from accidentally downgrading a schema.\n\nThe primary consumer is the Helm chart's pre-install and pre-upgrade Job, which runs `cyoda migrate` before starting the server to ensure the schema is up to date."
    },
    {
      "name": "OPTIONS",
      "body": "- `--timeout <duration>` — Maximum duration for the migration run (default: 5 minutes). Accepts Go duration strings: `30s`, `2m`, `10m`, etc."
    },
    {
      "name": "ENVIRONMENT VARIABLES",
      "body": "- `CYODA_STORAGE_BACKEND` — Selects the backend to migrate (default: `memory`).\n- `CYODA_POSTGRES_URL` — PostgreSQL DSN, required when backend is `postgres`. Accepts `CYODA_POSTGRES_URL_FILE` variant."
    },
    {
      "name": "EXIT CODES",
      "body": "- `0` — Migration succeeded (or was a no-op for memory/sqlite).\n- `1` — Runtime error: bad config, database unreachable, migration failure, or timeout.\n- `2` — Flag-parse error."
    },
    {
      "name": "EXAMPLES",
      "body": "```\n# Migrate postgres schema (reads CYODA_POSTGRES_URL from environment)\nCYODA_STORAGE_BACKEND=postgres \\\n  CYODA_POSTGRES_URL=\"postgres://user:pass@localhost/cyoda\" \\\n  cyoda migrate\n\n# Migrate with a custom timeout\nCYODA_STORAGE_BACKEND=postgres \\\n  CYODA_POSTGRES_URL=\"postgres://user:pass@localhost/cyoda\" \\\n  cyoda migrate --timeout 2m\n\n# No-op — memory backend\ncyoda migrate\n```"
    },
    {
      "name": "SEE ALSO",
      "body": "- config\n- cli.init"
    }
  ],
  "see_also": [
    "config",
    "cli.init"
  ],
  "stability": "stable",
  "actions": []
}
