Docker Migration: MacBook to Mac Studio Reference Guide
Prerequisites
Source machine: MacBook Pro (Apple Silicon) with Docker/OrbStack running
Destination machine: Mac Studio (or any macOS desktop)
Both machines on the same network (or Tailscale tailnet)
rsync installed on both machines
Phase 1: Export from Source Machine
export-for-migration.sh
#!/bin/bash
set -euo pipefail
EXPORT_DIR="$HOME/migration-export"
mkdir -p "$EXPORT_DIR"
# 1. Collect all .env files (including gitignored ones)
GITIGNORED_COMPOSE_FILES=(
"$HOME/projects/family-office/docker-compose.override.yml"
# Add other gitignored compose files here
)
find "$HOME/projects" -name ".env" -not -path "*/node_modules/*" \
-exec cp --parents {} "$EXPORT_DIR/" \;
for f in "${GITIGNORED_COMPOSE_FILES[@]}"; do
[[ -f "$f" ]] && cp --parents "$f" "$EXPORT_DIR/"
done
# 2. Validate .env completeness
for env_file in $(find "$EXPORT_DIR" -name ".env"); do
dir=$(dirname "$env_file")
example="$dir/.env.example"
if [[ -f "$example" ]]; then
# Flag variables in example but missing from .env
comm -23 \
<(grep -oP '^[A-Z_]+' "$example" | sort) \
<(grep -oP '^[A-Z_]+' "$env_file" | sort) \
| while read -r var; do
echo "WARNING: $var in .env.example but missing from $env_file"
done
fi
done
# 3. Export Docker volumes (ClickHouse + MinIO data)
echo "Exporting ClickHouse data..."
docker cp local-clickhouse:/var/lib/clickhouse/data "$EXPORT_DIR/clickhouse-data/"
docker cp local-clickhouse:/var/lib/clickhouse/metadata "$EXPORT_DIR/clickhouse-metadata/"
echo "Exporting MinIO data..."
docker cp langfuse-minio:/data "$EXPORT_DIR/minio-data/"
# 4. Postgres dumps (not just volume copy)
for container in $(docker ps --format '{{.Names}}' | grep postgres); do
echo "Dumping $container..."
docker exec "$container" pg_dumpall -U postgres \
> "$EXPORT_DIR/${container}-dump.sql"
done
# 5. Package
echo "Export complete: $EXPORT_DIR"
du -sh "$EXPORT_DIR"
rsync to destination
# From source machine
rsync -avz --progress \
"$HOME/migration-export/" \
user@mac-studio-tailscale-ip:~/migration-export/
Phase 2: Setup on Destination Machine
setup-mac-studio.sh
#!/bin/bash
set -euo pipefail
# 1. Homebrew
if ! command -v brew &>/dev/null; then
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# 2. Core tools
brew install --cask orbstack
brew install node python3 jq gh
# 3. OrbStack config
orb config set power.pause_in_sleep false
osascript -e 'quit app "OrbStack"'
sleep 3
open -a OrbStack
sleep 10
# 4. Tailscale (standalone pkg, NOT App Store)
# Download from https://tailscale.com/download/mac
# App Store version breaks tailscale serve (bundle identity issue)
# 5. If using App Store Tailscale, create wrapper script
cat > /usr/local/bin/tailscale << 'WRAPPER'
#!/bin/bash
exec /Applications/Tailscale.app/Contents/MacOS/Tailscale "$@"
WRAPPER
chmod +x /usr/local/bin/tailscale
Tailscale App Store fix
If you installed Tailscale from the App Store instead of the standalone pkg:
# Symlinks break tailscale serve (bundle identity error)
# DON'T do this:
# sudo ln -sf /Applications/Tailscale.app/Contents/MacOS/Tailscale /usr/local/bin/tailscale
# DO this instead (wrapper script with exec):
sudo tee /usr/local/bin/tailscale << 'EOF'
#!/bin/bash
exec /Applications/Tailscale.app/Contents/MacOS/Tailscale "$@"
EOF
sudo chmod +x /usr/local/bin/tailscale
Phase 3: Restore Data
ClickHouse restore
# 1. Copy data into container
docker cp ~/migration-export/clickhouse-data/ local-clickhouse:/var/lib/clickhouse/data/
docker cp ~/migration-export/clickhouse-metadata/ local-clickhouse:/var/lib/clickhouse/metadata/
# 2. CRITICAL: Remove stale .tmp files before restart
docker exec local-clickhouse bash -c \
'find /var/lib/clickhouse/metadata -name "*.sql.tmp" -delete'
# 3. Restart and verify
docker restart local-clickhouse
sleep 5
# 4. Verify all databases are visible
docker exec local-clickhouse clickhouse-client --query "SHOW DATABASES"
# 5. Spot-check row counts
docker exec local-clickhouse clickhouse-client \
--query "SELECT database, table, sum(rows) as row_count FROM system.parts WHERE active GROUP BY database, table ORDER BY database, table"
Postgres restore
# For each Postgres container
docker exec -i postgres-container psql -U postgres < ~/migration-export/postgres-container-dump.sql
# Verify schema exists
docker exec postgres-container psql -U postgres -c "\l"
docker exec postgres-container psql -U postgres -c "\dt"
MinIO restore
docker cp ~/migration-export/minio-data/ langfuse-minio:/data/
docker restart langfuse-minio
Phase 4: Post-Restore Verification
The full verification checklist
# 1. All containers healthy
docker ps --format "table {{.Names}}\t{{.Status}}" | grep -v healthy && echo "UNHEALTHY CONTAINERS FOUND" || echo "OK: all healthy"
# 2. ClickHouse databases present
docker exec local-clickhouse clickhouse-client --query "SHOW DATABASES"
# 3. ClickHouse row counts (spot check)
docker exec local-clickhouse clickhouse-client \
--query "SELECT count() FROM family_office.transactions"
# 4. Postgres schemas present
docker exec postgres-container psql -U postgres -c "\l"
# 5. Tailscale connected
tailscale status
# 6. Tailscale serve working (NOT just tailscale status)
tailscale serve status
# 7. .env completeness on destination
for env_file in $(find ~/projects -name ".env" -not -path "*/node_modules/*"); do
dir=$(dirname "$env_file")
example="$dir/.env.example"
if [[ -f "$example" ]]; then
missing=$(comm -23 \
<(grep -oP '^[A-Z_]+' "$example" | sort) \
<(grep -oP '^[A-Z_]+' "$env_file" | sort))
[[ -n "$missing" ]] && echo "MISSING in $env_file: $missing"
fi
done
# 8. End-to-end service test (not just "container is running")
curl -s http://localhost:8123/ping && echo "ClickHouse: OK" || echo "ClickHouse: FAIL"
curl -s http://localhost:3050/api/public/health && echo "Langfuse: OK" || echo "Langfuse: FAIL"
The Five Export Gaps
Quick Diagnostic Commands
# ClickHouse: database invisible after restore
docker exec local-clickhouse bash -c \
'ls /var/lib/clickhouse/metadata/*.sql.tmp 2>/dev/null'
# If files found: remove them, restart container
# Tailscale: serve crashes with bundle identity error
which tailscale && file $(which tailscale)
# If symlink: replace with wrapper script (see above)
# .env: container restart loop with undefined variables
docker logs --tail 20 container-name 2>&1 | grep -i "undefined\|missing\|blank"
# Check docker-compose.override.yml on source machine


