Last active
February 7, 2026 13:21
-
-
Save wiesson/9fcefdd04a81850a48389b3161066d67 to your computer and use it in GitHub Desktop.
Convex deployment migration script — exports data from one deployment and imports into another with --replace-all. Handles the .env.local precedence gotcha by swapping it temporarily, uses --prod on both export/import, and includes a full migration checklist (env vars, deploy, Stripe, Vercel).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # ─── Convex Deployment Migration ───────────────────────────────────────────── | |
| # Exports data from one Convex deployment and imports into another with | |
| # --replace-all. Handles the .env.local precedence gotcha by swapping it | |
| # temporarily, uses --prod on both export/import, and includes a full | |
| # migration checklist (env vars, deploy). | |
| # | |
| # Usage: | |
| # ./migrate.sh <source-deployment> <target-deployment> | |
| # | |
| # Example: | |
| # ./migrate.sh happy-panda-123 clever-fox-456 | |
| # | |
| # Run this from a directory that contains a convex/ folder. | |
| # | |
| # ─── Full Migration Checklist ──────────────────────────────────────────────── | |
| # | |
| # CAVEAT: The Convex CLI reads .env.local and it takes precedence over | |
| # CONVEX_DEPLOYMENT env vars. This script swaps .env.local | |
| # temporarily to work around this. | |
| # | |
| # NOTE: `convex export` only exports table data (+ file storage with | |
| # --include-file-storage). Code, crons, schema, and env vars are NOT | |
| # included — those are handled by steps 3 (env vars) and 4 (deploy). | |
| # | |
| # 1. Export data from source deployment (this script handles steps 1-2) | |
| # 2. Import data into target deployment with --prod | |
| # 3. Set env vars on target deployment: | |
| # Option A (Dashboard): Source → Settings → Environment Variables → copy all. | |
| # Target → paste/apply all. | |
| # Option B (CLI): npx convex env set VAR_NAME "value" --prod | |
| # (repeat for each var from source deployment) | |
| # 4. Deploy functions + crons: npx convex deploy | |
| # | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| EXPORT_FILE="convex-export.zip" | |
| ENV_LOCAL="convex/.env.local" | |
| ENV_LOCAL_BACKUP="convex/.env.local.bak" | |
| # Restore .env.local on exit (success or failure) | |
| cleanup() { | |
| if [[ -f "$ENV_LOCAL_BACKUP" ]]; then | |
| mv "$ENV_LOCAL_BACKUP" "$ENV_LOCAL" | |
| fi | |
| } | |
| trap cleanup EXIT | |
| # Swap .env.local to point at a specific deployment | |
| swap_env() { | |
| local deployment="$1" | |
| if [[ -f "$ENV_LOCAL" && ! -f "$ENV_LOCAL_BACKUP" ]]; then | |
| cp "$ENV_LOCAL" "$ENV_LOCAL_BACKUP" | |
| fi | |
| echo "CONVEX_DEPLOYMENT=$deployment" > "$ENV_LOCAL" | |
| } | |
| usage() { | |
| echo "Usage: ./migrate.sh <source-deployment> <target-deployment>" | |
| echo "" | |
| echo "Exports all data from the source Convex deployment and imports it" | |
| echo "into the target deployment using --replace-all." | |
| echo "" | |
| echo "Example:" | |
| echo " ./migrate.sh happy-panda-123 clever-fox-456" | |
| echo "" | |
| echo "Run this from a directory that contains a convex/ folder." | |
| } | |
| if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then | |
| usage | |
| exit 0 | |
| fi | |
| if [[ $# -ne 2 ]]; then | |
| usage | |
| exit 1 | |
| fi | |
| SOURCE="$1" | |
| TARGET="$2" | |
| if [[ ! -d "convex" ]]; then | |
| echo "Error: No convex/ folder found in the current directory." | |
| echo "Run this script from your Convex project root." | |
| exit 1 | |
| fi | |
| echo "╔══════════════════════════════════════════════════════════╗" | |
| echo "║ Convex Deployment Migration ║" | |
| echo "╚══════════════════════════════════════════════════════════╝" | |
| echo "" | |
| echo " Source: $SOURCE" | |
| echo " Target: $TARGET" | |
| echo "" | |
| # ─── Step 1: Export data from source deployment ───────────────────────────── | |
| echo "━━━ Step 1/2: Export data from $SOURCE ━━━" | |
| echo "" | |
| if [[ -f "$EXPORT_FILE" ]]; then | |
| echo " Export file already exists: $EXPORT_FILE" | |
| read -p " Skip export and reuse existing file? [Y/n] " skip_export | |
| if [[ "${skip_export:-Y}" =~ ^[Nn] ]]; then | |
| echo " Exporting..." | |
| swap_env "$SOURCE" | |
| npx convex export --path "$EXPORT_FILE" --prod | |
| cleanup | |
| echo " ✓ Export complete" | |
| else | |
| echo " ✓ Reusing existing export" | |
| fi | |
| else | |
| echo " Exporting data..." | |
| swap_env "$SOURCE" | |
| npx convex export --path "$EXPORT_FILE" --prod | |
| cleanup | |
| echo " ✓ Export complete" | |
| fi | |
| echo "" | |
| # ─── Step 2: Import data into target deployment ──────────────────────────── | |
| echo "━━━ Step 2/2: Import data into $TARGET ━━━" | |
| echo "" | |
| echo " ⚠ This will replace ALL data in $TARGET with --replace-all." | |
| read -p " Continue? [y/N] " confirm_import | |
| if [[ ! "${confirm_import:-N}" =~ ^[Yy] ]]; then | |
| echo " Aborted." | |
| exit 0 | |
| fi | |
| echo " Importing $EXPORT_FILE..." | |
| swap_env "$TARGET" | |
| npx convex import "$EXPORT_FILE" --replace-all --prod | |
| cleanup | |
| echo " ✓ Import complete" | |
| echo "" | |
| # ─── Follow-up reminders ─────────────────────────────────────────────────── | |
| echo "━━━ Next steps ━━━" | |
| echo "" | |
| echo " 1. Set environment variables on $TARGET" | |
| echo " npx convex env set KEY VALUE --deployment $TARGET" | |
| echo "" | |
| echo " 2. Deploy your functions" | |
| echo " CONVEX_DEPLOYMENT=$TARGET npx convex deploy" | |
| echo "" | |
| echo " 3. Update CONVEX_URL in your app's .env / hosting config" | |
| echo " https://$TARGET.convex.cloud" | |
| echo "" | |
| echo " 4. Verify your app works against the new deployment" | |
| echo "" | |
| # Clean up export file | |
| rm -f "$EXPORT_FILE" | |
| echo " ✓ Cleaned up $EXPORT_FILE" | |
| echo "" | |
| echo "╔══════════════════════════════════════════════════════════╗" | |
| echo "║ Migration complete! ║" | |
| echo "╚══════════════════════════════════════════════════════════╝" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment