- coruna-lab-migrate: server migration SOP (code/db/file transfer, supervisor setup, DNS cutover, verification, common pitfalls) - coruna-lab-cleanup: disk cleanup skill with disk_cleanup.sh and cleanup_scanned_photos.php scripts Co-authored-by: Cursor <cursoragent@cursor.com>
10 KiB
name, description
| name | description |
|---|---|
| coruna-lab-migrate | Migrate the coruna-lab (Coruna Lab) Laravel project from one server to another on BT Panel (宝塔面板). Covers code deployment, database dump/restore, file storage transfer (photos/ds-results/inbox), supervisor queue worker setup, DNS cutover, and post-migration verification. Use when the user asks to migrate coruna-lab to a new server, move coruna-lab to another machine, 换服务器, 迁移机器, 搬家, or set up a fresh coruna-lab deployment. Trigger keywords: coruna-lab 迁移, 迁移服务器, 换机器, migrate coruna-lab, server migration, 搬家, new server setup. |
coruna-lab Server Migration
Migrate the coruna-lab Laravel project between BT Panel (宝塔面板) servers.
Architecture overview
Old Server New Server
┌─────────────────────┐ ┌─────────────────────┐
│ BT Panel + Nginx │ │ BT Panel + Nginx │
│ PHP 8.2 │ rsync │ PHP 8.2 │
│ MySQL (coruna DB) │ ────────> │ MySQL (coruna DB) │
│ Redis │ │ Redis │
│ Supervisor (workers)│ │ Supervisor (workers)│
│ storage/app/private │ tar+ssh │ storage/app/private │
│ c2/photos/ (155G+) │ ────────> │ c2/photos/ │
└─────────────────────┘ └─────────────────────┘
Key paths & services
| Item | Path / Command |
|---|---|
| Project root | /www/wwwroot/coruna-lab |
| PHP | /www/server/php/82/bin/php |
| artisan | sudo -u www /www/server/php/82/bin/php artisan |
| Supervisor | sudo /www/server/panel/pyenv/bin/supervisorctl |
| Storage | storage/app/private/c2/{photos,ds-results,ds-chunks,ds-notes,inbox,photo-previews,plugin-sessions} |
| Logs | public/log/{xxbb,ds,transfer,c2}/ |
| Supervisor profiles | /www/server/panel/plugin/supervisor/profile/*.ini |
Migration phases
Phase 1: Prepare new server
- Install BT Panel, PHP 8.2, MySQL, Redis, Nginx on the new server.
- Create MySQL database and user:
CREATE DATABASE coruna CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'coruna'@'localhost' IDENTIFIED BY '<password>'; GRANT ALL ON coruna.* TO 'coruna'@'localhost'; FLUSH PRIVILEGES; - Create the site in BT Panel (point domain to
/www/wwwroot/coruna-lab).
Phase 2: Deploy code
The new server has no git — deploy via tarball from a machine that has the repo:
# On the machine with git access:
cd /www/wwwroot/coruna-lab
git archive --format=tar HEAD | gzip > /tmp/coruna-lab-code.tar.gz
scp /tmp/coruna-lab-code.tar.gz ubuntu@<new-server>:/tmp/
# On the new server:
sudo mkdir -p /www/wwwroot/coruna-lab
sudo tar -xzf /tmp/coruna-lab-code.tar.gz -C /www/wwwroot/coruna-lab
sudo chown -R www:www /www/wwwroot/coruna-lab
cd /www/wwwroot/coruna-lab
composer install --no-dev --optimize-autoloader
Phase 3: Database migration
# On old server: dump
mysqldump -ucoruna -p<old-pass> coruna --single-transaction --routines > /tmp/coruna.sql
scp /tmp/coruna.sql ubuntu@<new-server>:/tmp/
# On new server: import
mysql -ucoruna -p<new-pass> coruna < /tmp/coruna.sql
Run migrations on the new server:
cd /www/wwwroot/coruna-lab
sudo -u www /www/server/php/82/bin/php artisan migrate --force
Phase 4: Configure .env
Copy .env from old server, update for new server:
# Key settings to verify/update:
APP_URL=<new-domain>
DB_PASSWORD=<new-db-pass>
REDIS_HOST=127.0.0.1
QUEUE_CONNECTION=redis
# Keep these from old .env:
CORUNA_OFFICIAL_ALBUM_STORAGE=1
INTERCEPT_DEVICE_KEYS=...
INTERCEPT_BOT_TOKEN=...
INTERCEPT_CHAT_ID=...
Generate app key if needed (usually keep the old one):
sudo -u www /www/server/php/82/bin/php artisan key:generate
Phase 5: Transfer file storage
The c2/ directory can be 200G+. Use tar + ssh for reliability with large file counts:
#!/bin/bash
# transfer_locked.sh — run on OLD server
NEW_HOST="ubuntu@<new-server>"
SSH_KEY="/path/to/key.pem"
SRC="/www/wwwroot/coruna-lab/storage/app/private/c2"
DST="/www/wwwroot/coruna-lab/storage/app/private/c2"
for dir in photos photo-previews ds-chunks ds-notes ds-results plugin-sessions inbox; do
echo "Transferring $dir..."
tar -C "$SRC" -cf - "$dir" | ssh -i "$SSH_KEY" $NEW_HOST "sudo tar -C '$DST' -xf -"
done
Important: Transfer photos/ last (largest, 155G+). Use flock to prevent
duplicate runs. Monitor progress with find ... | wc -l on both servers.
Phase 6: Set up supervisor workers
This is the most critical step — missing workers cause silent data loss.
Create one .ini file per queue in /www/server/panel/plugin/supervisor/profile/:
| File | Queue | Command |
|---|---|---|
queue.ini |
default | artisan queue:work redis --sleep=1 --tries=3 --timeout=90 --max-time=3600 |
ocr.ini |
ocr | artisan queue:work redis --queue=ocr --sleep=1 --tries=1 --timeout=90 --max-jobs=100 |
keystore.ini |
keystore | artisan queue:work keystore --sleep=1 --tries=1 --timeout=320 --max-time=3600 |
telegram.ini |
telegram | artisan queue:work telegram --sleep=1 --tries=3 --timeout=30 --max-time=3600 |
transfer.ini |
transfer | artisan queue:work transfer --sleep=1 --tries=1 --timeout=200 --max-time=3600 |
extract.ini |
extract | artisan queue:work redis --queue=extract --sleep=1 --tries=1 --timeout=200 --max-jobs=100 |
⚠️ CRITICAL:
extract.iniis easily missed. Without it, photo archives pile up ininbox/and theextractRedis queue backs up indefinitely. Photos appear to stop storing even though/trequests keep arriving.
Template for extract.ini (others follow the same pattern):
[program:extract]
command=/www/server/php/82/bin/php -d memory_limit=256M artisan queue:work redis --queue=extract --sleep=1 --tries=1 --timeout=200 --max-jobs=100
directory=/www/wwwroot/coruna-lab/
autorestart=true
startsecs=3
startretries=3
stdout_logfile=/www/server/panel/plugin/supervisor/log/extract.out.log
stderr_logfile=/www/server/panel/plugin/supervisor/log/extract.err.log
stdout_logfile_maxbytes=2MB
stderr_logfile_maxbytes=2MB
user=www
priority=999
numprocs=2
process_name=%(program_name)s_%(process_num)02d
Load and start all workers:
sudo /www/server/panel/pyenv/bin/supervisorctl reread
sudo /www/server/panel/pyenv/bin/supervisorctl update
sudo /www/server/panel/pyenv/bin/supervisorctl start all
sudo /www/server/panel/pyenv/bin/supervisorctl status
Phase 7: Clear caches & restart PHP-FPM
cd /www/wwwroot/coruna-lab
sudo -u www /www/server/php/82/bin/php artisan config:clear
sudo -u www /www/server/php/82/bin/php artisan route:clear
sudo -u www /www/server/php/82/bin/php artisan view:clear
sudo -u www /www/server/php/82/bin/php artisan cache:clear
# Restart PHP-FPM
sudo /etc/init.d/php-fpm-82 restart
⚠️
view:clearis critical after code updates. Stale compiled Blade templates instorage/framework/views/cause 500 errors when new routes are referenced but old compiled cache doesn't have them.
Phase 8: DNS cutover
- Update Cloudflare DNS A record to new server IP.
- Wait for DNS propagation (or use Cloudflare proxy for instant cutover).
- Verify the site loads on the new server.
Phase 9: Verify
# Check site responds
curl -sI https://<domain>/ | head -5
# Check supervisor workers all RUNNING
sudo /www/server/panel/pyenv/bin/supervisorctl status
# Check Redis queue backlog (should be 0 or low for all queues)
cd /www/wwwroot/coruna-lab
sudo -u www /www/server/php/82/bin/php artisan tinker --execute='
$r = \Illuminate\Support\Facades\Redis::connection();
foreach (["default","extract","ocr","keystore","telegram","transfer"] as $q) {
echo "queues:$q = ".$r->llen("queues:$q")."\n";
}
'
# Check photos are being stored (should see recent timestamps)
mysql -ucoruna -p<pass> coruna -e '
SELECT COUNT(*) as cnt, MAX(created_at) as last
FROM photos WHERE created_at > DATE_SUB(NOW(), INTERVAL 10 MINUTE);
'
# Check inbox not backing up
ls /www/wwwroot/coruna-lab/storage/app/private/c2/inbox/ | wc -l
Common pitfalls
1. Missing extract queue worker (MOST COMMON)
Symptom: Users report photos stopped storing. /t requests arrive,
DB has few/no new photos, inbox/ directory grows, queues:extract in
Redis has 100K+ backlog.
Fix: Create extract.ini (see Phase 6), reload supervisor.
2. Stale Blade view cache causing 500 errors
Symptom: Pages return 500 after code update. Error log mentions route names not found in compiled view.
Fix: php artisan view:clear + restart PHP-FPM.
3. OCR workers restarting frequently
Symptom: ocr:ocr_00 and ocr:ocr_01 show very short uptimes
(seconds). Error log shows PHP module warnings ("Module already loaded").
Cause: PHP modules loaded twice (fileinfo, redis, zip, gmp). Usually harmless warnings but indicates PHP config issue. Workers still process jobs but may be slower.
4. Photo files not found after migration
Symptom: DB has photo records but files missing on disk.
Cause: Transfer script didn't complete, or path mismatch. Photos
are stored at storage/app/private/c2/photos/{device_uuid}/{sha256}_...,
NOT storage/app/private/photos/.
Fix: Re-run transfer for c2/photos/ directory. Verify with:
sudo find /www/wwwroot/coruna-lab/storage/app/private/c2/photos -type f | wc -l
5. Admin login locked after migration
Symptom: Can't log in to admin panel. login_attempts column shows
high value, locked_at is not NULL.
Fix:
UPDATE admins SET login_attempts=0, locked_at=NULL WHERE username='<user>';
Post-migration cleanup
After confirming the new server is stable:
- Stop old server supervisor workers:
/www/server/panel/pyenv/bin/supervisorctl stop all - Verify no traffic to old server (check nginx access logs).
- Decommission old server after 24-48 hours of stable operation.
- Run disk cleanup on new server (see
coruna-lab-cleanupskill).