utab / laravel-backup-kit
Encrypted, monitored database backups for Laravel on S3-compatible storage, built on spatie/laravel-backup.
Requires
- php: ^8.3
- ext-zip: *
- illuminate/console: ^12.40|^13.0
- illuminate/contracts: ^12.40|^13.0
- illuminate/database: ^12.40|^13.0
- illuminate/events: ^12.40|^13.0
- illuminate/filesystem: ^12.40|^13.0
- illuminate/http: ^12.40|^13.0
- illuminate/notifications: ^12.40|^13.0
- illuminate/routing: ^12.40|^13.0
- illuminate/support: ^12.40|^13.0
- league/flysystem-aws-s3-v3: ^3.30
- spatie/laravel-backup: ^10.3
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0|^5.0
Suggests
- deployer/deployer: Needed only for the optional pre-deploy backup task described in the README.
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-18 13:04:14 UTC
README
بکاپ رمزنگاریشده و پایششوندهی دیتابیس برای Laravel، روی هر ذخیرهساز سازگار با S3 (پیشفرض: ArvanCloud). ساختهشده روی spatie/laravel-backup.
پکیج را نصب میکنید، چند متغیر .env میگذارید و بقیه خودکار است: بکاپ شبانه، آرشیو AES-256، چیدمان تاریخدار، تاریخچهی اجراها، heartbeat به مانیتور مرکزی، هشدار و صفحهی عیبیابی storage.
فهرست
- امکانات
- نیازمندیها
- نصب سریع
- پیکربندی
- نحوهی کار
- دستورها
- زمانبندی
- وضعیت سلامت و heartbeat
- بکاپ ساعتی جدولهای حیاتی (hot backup)
- هشدار ایمیلی
- صفحهی تست storage
- Retention
- Restore
- بکاپ پیش از دیپلوی (Deployer)
- امنیت
- عیبیابی
- مهاجرت از پیادهسازی داخلی پروژه
- توسعه و تست
- مجوز
امکانات
- بکاپ شبانهی فقط دیتابیس (فایلهای پروژه هرگز بکاپ نمیشوند) در قالب zip با رمزنگاری AES-256.
- چیدمان تاریخدار روی باکت:
<project>/YYYY/MM/DD/<timestamp>.zip؛ چند پروژه میتوانند یک باکت مشترک داشته باشند. - تاریخچهی اجراها در جدول
backup_runsبرای هرbackup،cleanupوhot. - وضعیت سلامت پنجحالته (
SUCCESS،FAILED،STORAGE_ERROR،NEVER_RUN،STALE) و دستورbackup:statusبا exit code مناسب cron و alerting. - Heartbeat اختیاری به مانیتور مرکزی؛ بعد از هر شکست هم بلافاصله ارسال میشود.
- Hot backup ساعتی فقط برای جدولهای حیاتی (اختیاری).
- ایمیل هشدار اختیاری، لاگ اختصاصی
backupو پیامهای خطای sanitizeشده (بدون secret). - صفحهی تست storage برای عیبیابی اتصال به باکت.
- زمانبندی و پیکربندی خودکار؛ هر دو قابل خاموششدناند.
نیازمندیها
- PHP
^8.3با اکستنشنzip - Laravel
^12.40یا^13.0 spatie/laravel-backup^10.3وleague/flysystem-aws-s3-v3(همراه پکیج نصب میشوند)- باینری dump دیتابیس روی سرور (مثلاً
mysqldumpیاmariadb-dump) روی PATH یا از طریقDB_DUMP_BINARY_PATH - Scheduler لاراول فعال:
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
نصب سریع
composer require utab/laravel-backup-kit php artisan backup-kit:install php artisan migrate
backup-kit:install فایلهای config/backup-kit.php و config/backup.php (تنظیمات آمادهی Spatie) را publish میکند. فایلهای موجود دستنخورده میمانند، مگر با --force. معادل دستی:
php artisan vendor:publish --tag=backup-kit-config
php artisan vendor:publish --tag=backup-kit-spatie-config
php artisan vendor:publish --tag=backup-kit-migrations # فقط اگر migration را خودتان نگه میدارید
حداقل متغیرهای .env:
BACKUP_PROJECT=my-project # پوشهی ریشهی این پروژه روی باکت (پیشفرض: APP_NAME)، در باکت مشترک یکتا باشد BACKUP_ARCHIVE_PASSWORD= # رمز AES-256 آرشیو، الزامی ARVAN_ACCESS_KEY= ARVAN_SECRET_KEY= ARVAN_BUCKET= ARVAN_ENDPOINT=https://s3.ir-central1.arvanstorage.ir # بدون نام باکت در host
بعد از تنظیم، نصب را بررسی کنید:
php artisan backup:status # دیسک reachable است؟ php artisan backup:run --only-db # اولین بکاپ دستی php artisan backup:status # باید SUCCESS شود
اگر از قبل
config/backup.phpدارید و میخواهید نگهش دارید:source.files.includeباید خالی باشد،passwordوencryption => 'aes256'تنظیم شود،destination.disksشاملBACKUP_DISKباشد وbackup.nameهمانBACKUP_PROJECTباشد. سادهترین راه شروع از stub پکیج است.
پیکربندی
همهی تنظیمات پکیج در config/backup-kit.php است و تقریباً همه از .env خوانده میشوند. تنظیمات Spatie (retention، health checkها، notifications) در config/backup.php میمانند.
متغیرهای محیطی
ذخیرهسازی و هویت پروژه
| متغیر | پیشفرض | توضیح |
|---|---|---|
BACKUP_DISK |
arvan-backup |
نام دیسک مقصد |
BACKUP_PROJECT |
APP_NAME |
پوشهی ریشهی پروژه روی باکت و برچسب آن در مانیتور |
BACKUP_ARCHIVE_PASSWORD |
- | رمز آرشیو (AES-256)؛ بدون آن health check ArchiveIsEncrypted شکست میخورد |
ARVAN_ACCESS_KEY / ARVAN_SECRET_KEY |
- | credential سرویس Object Storage |
ARVAN_BUCKET |
- | نام باکت |
ARVAN_ENDPOINT |
- | endpoint S3؛ بدون نام باکت |
ARVAN_REGION |
default |
region |
ARVAN_USE_PATH_STYLE_ENDPOINT |
true |
آدرسدهی path-style |
BACKUP_RUNS_TABLE |
backup_runs |
نام جدول تاریخچه (قبل از migrate تنظیم شود) |
BACKUP_KIT_LOAD_MIGRATIONS |
true |
بارگذاری خودکار migration پکیج؛ بعد از publish دستی false کنید |
Dump دیتابیس
| متغیر | پیشفرض | توضیح |
|---|---|---|
DB_DUMP_BINARY_PATH |
خالی | پوشهی حاوی mysqldump (مثل /usr/bin)، نه خود باینری |
DB_DUMP_SINGLE_TRANSACTION |
true |
dump سازگار بدون قفل جدول (InnoDB) |
DB_DUMP_TIMEOUT |
300 |
timeout به ثانیه |
DB_DUMP_EXCLUDE_TABLES |
خالی | جدولهای حذفشونده، جداشده با کاما. جدول بهکلی (schema و data) حذف میشود و هنگام restore ساخته نمیشود |
زمانبندی، مانیتور و hot backup
| متغیر | پیشفرض | توضیح |
|---|---|---|
BACKUP_SCHEDULE_ENABLED |
true |
ثبت خودکار زمانبندی |
BACKUP_DAILY_AT |
02:30 |
ساعت بکاپ شبانه |
BACKUP_CLEANUP_AT |
04:00 |
ساعت cleanup (retention) |
BACKUP_REPORT_EVERY_MINUTES |
15 |
فاصلهی heartbeat (۱ تا ۵۹) |
BACKUP_MONITOR_ENABLED |
true |
کلید کلی مانیتور |
BACKUP_MONITOR_URL |
- | endpoint heartbeat؛ بدون مقدار، هیچ heartbeatی ارسال نمیشود |
BACKUP_MONITOR_TOKEN |
- | Bearer token |
BACKUP_MONITOR_TIMEOUT |
10 |
timeout درخواست به ثانیه |
BACKUP_STALE_AFTER_HOURS |
30 |
بعد از این مدت بدون موفقیت، وضعیت STALE میشود |
BACKUP_HOT_TABLES |
خالی | جدولهای حیاتی، جداشده با کاما؛ خالی یعنی hot backup غیرفعال |
BACKUP_HOT_ENABLED |
true |
کلید کلی hot backup |
BACKUP_HOT_FROM_HOUR / BACKUP_HOT_TO_HOUR |
9 / 23 |
بازهی کاری اجرای ساعتی |
BACKUP_HOT_KEEP_DAYS |
7 |
نگهداری آرشیوهای hot (0 یعنی حذف نکن) |
صفحهی تست، لاگ، retention و اعلان
| متغیر | پیشفرض | توضیح |
|---|---|---|
BACKUP_STORAGE_TEST_ENABLED |
true |
ثبت route صفحهی تست storage |
BACKUP_STORAGE_TEST_PATH |
backup-storage-test |
مسیر route |
BACKUP_DEBUG_TOKEN |
- | توکن صفحهی تست خارج از local (fallback: BACKUP_MONITOR_TOKEN) |
BACKUP_NOTIFICATION_MAIL_TO |
- | گیرندهی ایمیل هشدار؛ خالی یعنی ایمیل خاموش |
BACKUP_LOG_LEVEL / BACKUP_LOG_DAYS |
info / 30 |
سطح و مدت نگهداری لاگ storage/logs/backup-*.log |
BACKUP_KEEP_ALL_DAYS |
7 |
نگهداری همهی بکاپها |
BACKUP_KEEP_DAILY_DAYS |
16 |
یک بکاپ در روز |
BACKUP_KEEP_WEEKLY_WEEKS |
8 |
یک بکاپ در هفته |
BACKUP_KEEP_MONTHLY_MONTHS |
4 |
یک بکاپ در ماه |
BACKUP_KEEP_YEARLY_YEARS |
2 |
یک بکاپ در سال |
BACKUP_MAX_STORAGE_MB |
20000 |
سقف فضا؛ قدیمیترین بکاپها بعد از آن حذف میشوند و باید زیر سهمیهی باکت بماند |
BACKUP_MAX_AGE_DAYS |
2 |
health check: بیشینهی سن جدیدترین بکاپ |
BACKUP_VERIFY_ARCHIVE |
true |
بررسی آرشیو بعد از ساخت |
BACKUP_TRIES / BACKUP_RETRY_DELAY_SECONDS |
2 / 60 |
تعداد تلاش و فاصلهی بین تلاشها |
تنظیم خودکار (auto-configure)
پکیج در boot این سه مورد را تعریف میکند، هرکدام فقط اگر اپ خودش تعریفش نکرده باشد:
- دیسک S3 با نام
BACKUP_DISKکه از متغیرهایARVAN_*تغذیه میشود. - کانال لاگ
backup(روزانه، فایلstorage/logs/backup-YYYY-MM-DD.log). - تنظیمات
dumpروی اتصالهای دیتابیس لیستشده درbackup.source.databases(single-transaction، timeout، exclude tables، مسیر باینری). رمز دیتابیس از طریق فایل defaults موقت بهmysqldumpمیرسد و روی خط فرمان نمیآید.
هرکدام با backup-kit.auto_configure.{disk,log_channel,dump}.enabled خاموش میشود. تعریفها داخل فایل config هستند، پس php artisan config:cache آنها را هم cache میکند. برای سفارشیسازی (مثلاً دیسک غیر Arvan) کافی است دیسک را خودتان در config/filesystems.php تعریف کنید.
نحوهی کار
php artisan backup:run --only-db (زمانبندیشده، ۰۲:۳۰)
│
▼
dump.sql ──► ZIP (AES-256 + BACKUP_ARCHIVE_PASSWORD) ──► verify
│
▼
<ARVAN_BUCKET>/<BACKUP_PROJECT>/YYYY/MM/DD/<timestamp>.zip
│
├── backup_runs (DB) ──► SUCCESS / FAILED / STORAGE_ERROR / NEVER_RUN / STALE
└── heartbeat ──► BACKUP_MONITOR_URL (اختیاری)
- مسیر تاریخدار هنگام زمانبندی با
--destination-pathبهbackup:runداده میشود. ListenerMoveBackupToDatedFolderروی رویدادBackupWasSuccessfulهر آرشیو flat را (از جمله اجراهای دستی) بهY/m/dمنتقل میکند؛ idempotent است و فایل موجود را overwrite نمیکند. - Listener
RecordBackupRunEventsبعد از جابهجایی، اجرا را درbackup_runsثبت و heartbeat را ارسال میکند. خطای ثبت یا مانیتور هرگز pipeline بکاپ را نمیشکند. - پیام خطا قبل از ذخیره یا لاگ از
BackupErrorSanitizerعبور میکند.
دستورها
| دستور | توضیح |
|---|---|
backup-kit:install [--force] [--migrations] |
publish تنظیمات (و اختیاری migration) و چاپ مراحل باقیمانده |
backup:status [--json] [--skip-storage] |
وضعیت سلامت؛ exit code صفر فقط برای SUCCESS. --skip-storage پروب دیسک را رد میکند |
backup:report [--dry-run] |
محاسبه و ارسال heartbeat؛ --dry-run فقط payload را چاپ میکند |
backup:hot [--tables=a,b] [--from-file=dump.sql] [--list] |
بکاپ جدولهای حیاتی؛ --list جدولهای تنظیمشده را نشان میدهد |
دستورهای خود Spatie هم کار میکنند: backup:run، backup:clean، backup:list و backup:monitor.
php artisan backup:run --only-db php artisan backup:list php artisan backup:clean # destination-path کاملاً جای نام پروژه مینشیند: پیشوند پروژه را خودتان بدهید php artisan backup:run --only-db --destination-path="my-project/manual/$(date +%Y/%m/%d)"
زمانبندی
مگر با BACKUP_SCHEDULE_ENABLED=false، چهار ورودی روی scheduler اپ ثبت میشود. همه با withoutOverlapping() و بر اساس timezone اپ اجرا میشوند:
| نام | زمان | شرط |
|---|---|---|
backup-run-daily |
BACKUP_DAILY_AT |
backup:run --only-db --destination-path=<project>/Y/m/d |
backup-hot-hourly |
هر ساعت بین BACKUP_HOT_FROM_HOUR و BACKUP_HOT_TO_HOUR |
فقط وقتی BACKUP_HOT_TABLES پر و BACKUP_HOT_ENABLED روشن باشد |
backup-clean-daily |
BACKUP_CLEANUP_AT |
backup:clean |
backup-monitor-heartbeat |
هر BACKUP_REPORT_EVERY_MINUTES دقیقه |
فقط وقتی BACKUP_MONITOR_URL تنظیم و مانیتور روشن باشد |
مقدار نامعتبر هرگز scheduler را خراب نمیکند: ساعت بدفرمت به پیشفرض برمیگردد، ساعتهای hot به بازهی ۰ تا ۲۳ محدود میشوند و فاصلهی heartbeat به ۱ تا ۵۹ دقیقه. اگر BACKUP_HOT_FROM_HOUR بزرگتر از BACKUP_HOT_TO_HOUR باشد، ورودی hot کلاً اجرا نمیشود (بازهی شبانهروزی پشتیبانی نمیشود).
اگر زمانبندی را خودتان میخواهید (BACKUP_SCHEDULE_ENABLED=false):
// routes/console.php use Illuminate\Support\Facades\Schedule; Schedule::command('backup:run', ['--only-db'])->dailyAt('02:30')->withoutOverlapping(); Schedule::command('backup:clean')->dailyAt('04:00')->withoutOverlapping(); Schedule::command('backup:report')->everyFifteenMinutes();
وضعیت سلامت و heartbeat
| وضعیت | معنا |
|---|---|
SUCCESS |
آخرین بکاپ موفق و تازه است |
FAILED |
آخرین اجرا ناموفق بود (dump، zip یا pipeline) |
STORAGE_ERROR |
دیسک در دسترس نیست یا آخرین اجرا در لایهی storage شکست خورد |
NEVER_RUN |
هیچ اجرایی ثبت نشده (یا جدول تاریخچه migrate نشده) |
STALE |
آخرین موفقیت قدیمیتر از BACKUP_STALE_AFTER_HOURS است |
اولویت: STORAGE_ERROR ← FAILED ← STALE ← NEVER_RUN ← SUCCESS. فقط اجراهای نوع backup در وضعیت اصلی حساب میشوند؛ اجراهای cleanup و hot آن را خراب نمیکنند.
اگر BACKUP_MONITOR_URL تنظیم باشد، پکیج heartbeat را هر چند دقیقه، بعد از هر رویداد پایانی بکاپ و بلافاصله بعد از هر شکست hot ارسال میکند:
POST {BACKUP_MONITOR_URL} با هدر Authorization: Bearer {BACKUP_MONITOR_TOKEN} و بدنهی JSON:
{
"project": "my-project",
"environment": "production",
"status": "SUCCESS",
"detail": "Last backup completed successfully.",
"checked_at": "2026-10-06T15:00:00+03:30",
"last_run_at": "2026-10-06T02:30:04+03:30",
"last_success_at": "2026-10-06T02:30:58+03:30",
"last_backup_path": "my-project/2026/10/06/2026-10-06-02-30-00.zip",
"last_backup_size_bytes": 12345678,
"disk": "arvan-backup",
"consecutive_failures": 0,
"hot": {
"status": "success",
"last_run_at": "2026-10-06T15:00:03+03:30",
"last_success_at": "2026-10-06T15:00:03+03:30",
"last_path": "hot-my-project/2026/10/06/2026-10-06-15-00-03-hot.zip",
"last_size_bytes": 54210,
"consecutive_failures": 0
},
"meta": { "php": "8.4.0", "laravel": "13.0.0" }
}
برای دیدن payload بدون ارسال: php artisan backup:report --dry-run. کلاینت مانیتور هرگز exception پرتاب نمیکند؛ خرابی مانیتور فقط یک warning در لاگ backup است.
بکاپ ساعتی جدولهای حیاتی (hot backup)
برای دادهی جایگزینناپذیر (سفارشها، کیف پول، کاربران، نقشها و دسترسیها و...)، فولدامپ شبانه RPO یک روزه دارد. Hot backup هر ساعت در بازهی کاری فقط همین جدولها را dump میکند؛ آرشیو بسیار کوچک است و RPO دادهی حیاتی به یک ساعت میرسد. این قابلیت تا وقتی جدولها را تنظیم نکنید خاموش است:
BACKUP_HOT_TABLES=orders,wallet_transactions,users
hot-<BACKUP_PROJECT>/YYYY/MM/DD/<timestamp>-hot.zip ← AES-256، روی همان دیسک
- چرا
hot-<project>/در ریشه؟ Retention پکیج Spatie لیستallFiles(<project>)را prefix-match میکند. اگر آرشیو hot داخل<project>/یا<project>-hot/بود، keep-daily ممکن بود یک zip کوچک hot را بهجای فولدامپ نگه دارد. پیشوندhot-این تداخل را میشکند. - Retention جدا: بعد از هر اجرای موفق، آرشیوهای قدیمیتر از
BACKUP_HOT_KEEP_DAYSحذف میشوند. - ثبت: در
backup_runsباtype = 'hot'؛ وضعیت اصلی بکاپ را تغییر نمیدهد. وضعیت مستقل آن در بلوکhotheartbeat است:disabled | never_run | success | failed | storage_error | stale. وضعیتstaleوابسته به بازهی کاری است: داخل بازه، آخرین موفقیت باید کمتر از ۲ ساعت قبل باشد؛ بیرون بازه کافی است آخرین اسلات دیروز اجرا شده باشد. - هشدار: شکست در لاگ ثبت میشود، heartbeat فوری میرود و (اگر ایمیل فعال باشد) فقط در اولین شکستِ هر دنباله ایمیل میرسد؛ اولین موفقیت بعدی دنباله را ریست میکند.
- اتصال: از اولین اتصال
backup.source.databases(یاdatabase.default) استفاده میشود و فایل داخل zip با نامdb-dumps/<db>-hot.sqlذخیره میشود. - اگر
BACKUP_ARCHIVE_PASSWORDتنظیم نباشد، آرشیو hot بدون رمز نوشته میشود و یک warning در لاگ میآید.
هشدار ایمیلی
ایمیل اختیاری است و با تنظیم BACKUP_NOTIFICATION_MAIL_TO روشن میشود. آنوقت اعلانهای شکست Spatie (BackupHasFailed، CleanupHasFailed، UnhealthyBackupWasFound) و اعلان HotBackupFailed پکیج به آن آدرس میروند. بدون این متغیر، heartbeat تنها منبع حقیقت است. فرستنده از MAIL_FROM_ADDRESS و MAIL_FROM_NAME خوانده میشود.
صفحهی تست storage
GET /backup-storage-test سه پروب روی دیسک بکاپ اجرا میکند و خطای دقیقِ sanitizeشده را نشان میدهد:
- کاملبودن تنظیمات دیسک (برای دیسک
s3: key، secret، endpoint و bucket) files()روی پوشهی پروژه (احراز هویت و خواندن)- round-trip
put/get/delete(دسترسی نوشتن)
خروجی HTML است؛ با Accept: application/json پاسخ JSON میدهد. در موفقیت 200 و در شکست 503 برمیگرداند.
- Local: بدون توکن باز است.
- غیر Local: فقط با
?token=برابرBACKUP_DEBUG_TOKEN(در نبود آنBACKUP_MONITOR_TOKEN)؛ در غیر این صورت404. اگر هیچ توکنی تنظیم نشده باشد، همیشه404. - access key و secret هرگز چاپ نمیشوند؛ فقط
set (N chars)یاMISSINGدیده میشود. برای دیسکهای غیرs3فقط وجود تعریف دیسک بررسی میشود.
curl -H "Accept: application/json" "https://example.com/backup-storage-test?token=$BACKUP_DEBUG_TOKEN"
مسیر، middleware و روشن/خاموشبودن route در config/backup-kit.php است:
'routes' => [ 'storage_test' => [ 'enabled' => env('BACKUP_STORAGE_TEST_ENABLED', true), 'path' => env('BACKUP_STORAGE_TEST_PATH', 'backup-storage-test'), 'middleware' => ['web'], ], ],
نام route برابر backup-kit.storage-test است.
Retention
Cleanup با DefaultStrategy پکیج Spatie انجام میشود و جدیدترین بکاپ همیشه نگه داشته میشود. بازهها با متغیرهای BACKUP_KEEP_* و BACKUP_MAX_STORAGE_MB (جدول بالا) تنظیم میشوند. Cleanup روی پوشهی پروژه recursive کار میکند، پس فایلهای داخل Y/m/d و pre-deploy/ هم پوشش داده میشوند. آرشیوهای hot retention جدا دارند (BACKUP_HOT_KEEP_DAYS).
Restore
جدولهای
DB_DUMP_EXCLUDE_TABLESدر dump نیستند و بعد از restore ساخته نمیشوند؛ باید با migration یا دستی دوباره ساخته شوند.
۱. آرشیو را پیدا و دانلود کنید:
php artisan backup:list aws --endpoint-url "$ARVAN_ENDPOINT" s3 ls "s3://$ARVAN_BUCKET/my-project/" --recursive aws --endpoint-url "$ARVAN_ENDPOINT" s3 cp \ "s3://$ARVAN_BUCKET/my-project/2026/10/06/2026-10-06-02-30-00.zip" backup.zip
۲. رمزگشایی: unzip معمولی AES-256 را نمیفهمد؛ از 7-Zip (لینوکس: 7zz یا p7zip) یا PHP استفاده کنید:
7z x -p"$BACKUP_ARCHIVE_PASSWORD" backup.zip -o./restore-tmp
$zip = new ZipArchive; $zip->open('backup.zip', ZipArchive::RDONLY); $zip->setPassword(getenv('BACKUP_ARCHIVE_PASSWORD')); $zip->extractTo('./restore-tmp'); $zip->close();
۳. دیتابیس را روی یک دیتابیس جدید یا staging بازیابی کنید، نه مستقیم روی production:
mysql -h <host> -u <user> -p <database> < ./restore-tmp/db-dumps/mysql-<database>.sql
۴. بعد از restore، تعداد ردیفهای جدولهای کلیدی را بررسی کنید و مطمئن شوید اپ بالا میآید (php artisan about).
Restore از hot backup: ابتدا فولدامپ شب را restore کنید، سپس آخرین آرشیو hot را با همان روش باز کنید و db-dumps/<db>-hot.sql را import کنید. این کار روی جدولهای موجود overwrite میکند (mysqldump بهصورت پیشفرض DROP TABLE IF EXISTS دارد، پس schema هم درست میشود).
بکاپ پیش از دیپلوی (Deployer)
پکیج هیچ کدی برای Deployer ندارد و deployer/deployer وابستگی آن نیست. اگر دیپلوی شما با Deployer انجام میشود، این تسک قبل از migration یک dump تازه از release فعلی (current، یعنی کد و .env که میدانید کار میکند) روی باکت میگذارد تا یک migration خراب هرگز بدون snapshot اجرا نشود:
<?php namespace Deployer; desc('Pre-deploy database backup (deploy aborts on failure)'); task('backup:pre-deploy', function () { if (getenv('DEPLOY_SKIP_BACKUP')) { writeln('<comment>DEPLOY_SKIP_BACKUP set, skipping pre-deploy backup</comment>'); return; } // --destination-path replaces the backup name entirely, so the project // prefix must be part of it: {{application}} has to equal BACKUP_PROJECT. run('cd {{current_path}} && {{bin/php}} artisan backup:run --only-db --destination-path="{{application}}/pre-deploy/$(date +%Y/%m/%d)"'); }); before('artisan:migrate', 'backup:pre-deploy');
- آرشیو در
<project>/pre-deploy/YYYY/MM/DD/مینشیند و زیر retention عادی است. - اگر بکاپ شکست بخورد، دستور
runخطا میدهد و دیپلوی متوقف میشود. - دور زدن فقط برای اورژانس:
DEPLOY_SKIP_BACKUP=1. در اولین دیپلوی هم releasecurrentوجود ندارد و باید از همین متغیر استفاده کنید.
امنیت
- credentialها فقط از
.envخوانده میشوند و هیچجا چاپ نمیشوند. - پیام خطا قبل از ذخیره در
backup_runs.error_summaryیا لاگ: فقط خط اول، کوتاهشده، با حذفpassword=/token=/secret=/key=و mask کردن secretهای جاری config (دیتابیس، S3، رمز آرشیو، توکن مانیتور؛ حداقل ۸ کاراکتر). - رمز دیتابیس از طریق فایل defaults موقت به
mysqldumpمیرسد و در argv یا لیست پردازهها دیده نمیشود. - رمز آرشیو را در یک secret store نگه دارید. هر آرشیو با رمزِ زمان ساختش رمز میشود؛ هنگام تغییر رمز، رمز قبلی را تا حذف شدن آرشیوهای قدیمی نگه دارید، وگرنه آنها قابل بازیابی نیستند.
- توکن صفحهی تست storage در URL میآید و ممکن است در access logها بماند؛ آن را دورهای عوض کنید یا اگر لازم نیست
BACKUP_STORAGE_TEST_ENABLED=falseبگذارید. - کلید S3 را فقط به باکت (و در صورت امکان پیشوند) بکاپ محدود کنید؛ دسترسی list، put، get و delete کافی است (delete برای cleanup لازم است).
- آدرس مانیتور را فقط HTTPS بدهید؛ توکن بهصورت Bearer ارسال میشود.
عیبیابی
| علامت | بررسی |
|---|---|
STORAGE_ERROR |
صفحهی /backup-storage-test را باز کنید (در production با ?token=)؛ خطای دقیق S3 را نشان میدهد |
NoSuchKey |
ARVAN_ENDPOINT نباید نام باکت را داخل host داشته باشد (https://s3.ir-central1.arvanstorage.ir درست است). با path-style، نام باکت در host یعنی «double bucket» |
Disk [...] does not have a configured driver |
دیسک BACKUP_DISK تعریف نشده: ARVAN_* را تنظیم کنید یا دیسک را در config/filesystems.php بسازید |
خطای mysqldump |
DB_DUMP_BINARY_PATH باید پوشهی حاوی باینری باشد (مثل /usr/bin)، نه خود باینری |
NEVER_RUN |
Scheduler (schedule:run) فعال است؟ migration جدول backup_runs اجرا شده؟ |
STALE |
آخرین خطا را ببینید: SELECT * FROM backup_runs ORDER BY id DESC LIMIT 5 و storage/logs/backup-*.log |
health check ArchiveIsEncrypted |
BACKUP_ARCHIVE_PASSWORD تنظیم نشده یا encryption در config/backup.php دیگر aes256 نیست |
| heartbeat نمیرسد | backup:report --dry-run payload را نشان میدهد؛ BACKUP_MONITOR_URL و BACKUP_MONITOR_ENABLED را بررسی و لاگ backup را نگاه کنید |
مهاجرت از پیادهسازی داخلی پروژه
اگر پروژهای همین pipeline را بهصورت کد داخلی دارد، با نصب پکیج این جایگزینیها لازم است:
| قبلاً (در اپ) | با پکیج |
|---|---|
بلوکهای monitoring، schedule و hot در config/backup.php |
همان کلیدها زیر config/backup-kit.php |
backup.monitoring.disk |
backup-kit.disk (BACKUP_DISK) |
دیسک arvan-backup، کانال backup و dump در filesystems، logging و database |
خودکار، اگر خودتان تعریف نکرده باشید |
ورودیهای Schedule در routes/console.php |
خودکار؛ با BACKUP_SCHEDULE_ENABLED=false خاموش میشود |
| route و controller صفحهی تست storage | backup-kit.routes.storage_test |
| فهرست پیشفرض جدولهای hot در config | باید BACKUP_HOT_TABLES را صریح بدهید (پیشفرض خالی یعنی خاموش) |
Migration پکیج idempotent است: اگر جدول backup_runs از قبل وجود داشته باشد، دستنخورده میماند. کد داخلی (provider، commandها، listenerها، controller و route) را بعد از اطمینان از کارکرد پکیج حذف کنید.
توسعه و تست
composer install composer test # Pest روی Orchestra Testbench؛ نیازی به S3 یا شبکه ندارد composer lint # Laravel Pint در حالت --test
تستها دیسک و HTTP را با Storage::fake و Http::fake شبیهسازی میکنند.
مجوز
MIT. متن کامل در فایل LICENSE.