Skip to content

Instantly share code, notes, and snippets.

@wiesson
Last active February 7, 2026 13:21
Show Gist options
  • Select an option

  • Save wiesson/9fcefdd04a81850a48389b3161066d67 to your computer and use it in GitHub Desktop.

Select an option

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).
#!/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