nyoncode/laravel-backup-manager

Powerful and extensible backup manager for Laravel applications: databases, files, encryption, integrity manifests and multi-destination storage.

Maintainers

Package info

github.com/NyonCode/laravel-backup-manager

pkg:composer/nyoncode/laravel-backup-manager

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-07-17 03:52 UTC

This package is auto-updated.

Last update: 2026-07-17 06:25:34 UTC


README

Tests PHPStan PHP Version License

A powerful, extensible and CLI-first backup manager for Laravel 12+ and 13: databases, files, AES-256 encryption, integrity manifests and multi-destination storage β€” with restore, retention, scheduling, queues and monitoring built in.

It is designed around composition over inheritance: every capability (dumping, compressing, encrypting, hashing, archiving, storing) is a small, replaceable unit behind an interface, so it is easy to test, extend and to build a UI on top of.

πŸ‡¨πŸ‡Ώ KompletnΓ­ českΓ‘ dokumentace: docs/cz. The badge URLs assume github.com/nyoncode/laravel-backup-manager β€” adjust if your repository lives elsewhere.

Table of contents

Why this package

Most Laravel backup tools stop at "zip the app and push it to S3". Backup Manager goes further:

  • Every backup is self-describing. A JSON manifest records the exact environment, drivers, archive checksum and a hash of every file β€” so a backup is verifiable and portable independently of this package.
  • Safe restores. Restores verify integrity first, and by default take a pre-restore snapshot so a failed restore rolls back to the previous state.
  • No arbitrary size limits. The archiver is a streaming TAR implementation with GNU extensions: no 8 GiB per-file limit, no 100-byte path limit, flat memory usage.
  • Real multi-destination. Destinations are ordinary Laravel disks, so Local, S3, FTP, SFTP, Azure, B2 and WebDAV all work, and you can write to several at once.
  • Production plumbing included. Queues, the scheduler (with withoutOverlapping), GFS retention, health monitoring, an operation-history table and mail/Slack notifications.

Requirements

Requirement Version
PHP 8.3+ (Laravel 13 needs 8.4+)
Laravel 12.x / 13.x
Extensions ext-openssl, ext-json; optional ext-bz2 for bzip2

Database dumps use the vendor CLI tools:

Driver Dump Restore
MySQL / MariaDB mysqldump mysql
PostgreSQL pg_dump psql
SQLite file copy file copy

zstd compression needs the zstd binary. Remote disks may need their Flysystem adapter (e.g. league/flysystem-aws-s3-v3).

Installation

composer require nyoncode/laravel-backup-manager

Publish the configuration:

php artisan vendor:publish --tag="backup-manager-config"

Optionally publish and run the history migration (see Monitoring):

php artisan vendor:publish --tag="backup-manager-migrations"
php artisan migrate

Full details: docs/installation.md.

Quick start

php artisan backup:run          # create a backup (default profile)
php artisan backup:list         # list what's stored
php artisan backup:verify       # verify the newest backup's integrity
php artisan backup:restore      # restore the newest backup (asks to confirm)
php artisan backup:clean        # apply the retention policy
php artisan backup:monitor      # report backup health

Core concepts

Concept What it is
Profile A named backup definition (what to back up, where to store it). The default profile is used when none is given. You can define as many as you like.
Backup One compressed, optionally encrypted TAR archive plus a JSON manifest sidecar.
Destination A Laravel filesystem disk. A profile may write to several at once.
Retention The policy that decides which historical backups to keep.
Restore Bringing a backup back β€” always after verifying integrity, with an optional rollback safety net.

A profile lives in config/backup-manager.php:

'profiles' => [
    'default' => [
        'name' => env('APP_NAME', 'Laravel'),
        'type' => 'full',                       // full | database | files
        'databases' => ['connections' => []],   // [] = default connection
        'files' => [
            'include' => [base_path()],
            'exclude' => [base_path('vendor'), base_path('node_modules'), base_path('.git')],
        ],
        'destinations' => ['local'],             // Laravel disk names
        'path' => 'backups',
    ],
],

Run a specific profile with --profile=name. See docs/configuration.md for every option.

What a backup contains

A single backup produces two objects on each destination:

backups/default-2026-07-16-020000-a1b2c3.tar.gz.enc     ← the archive
backups/default-2026-07-16-020000-a1b2c3.manifest.json  ← the manifest sidecar

Inside the (uncompressed, pre-encryption) archive:

databases/mysql.sql              ← one dump per configured connection
files/var/www/app/Models/User.php ← files mirror their absolute path
files/var/www/.env

The filename carries the profile, a timestamp and a short random suffix so two backups never collide, even within the same second.

The backup manifest

The manifest is the feature that makes a backup self-describing and verifiable. Because it is stored unencrypted next to the archive, you can inspect it and check the archive's checksum without downloading or decrypting the archive.

{
    "schema_version": "1.0",
    "name": "Acme",
    "type": "full",
    "created_at": "2026-07-16T02:00:00+00:00",
    "environment": {
        "app_name": "Acme", "app_version": "1.4.2",
        "laravel_version": "12.0.0", "php_version": "8.3.30",
        "environment": "production", "operating_system": "Linux 6.1.0"
    },
    "drivers": { "compression": "gzip", "encryption": "aes-256-gcm", "checksum": "sha256" },
    "databases": [
        { "connection": "mysql", "driver": "mysql", "database_name": "acme", "archived_path": "databases/mysql.sql" }
    ],
    "archive": { "size": 10485760, "checksum": { "algorithm": "sha256", "digest": "…" } },
    "files": [
        { "path": "files/var/www/app/Models/User.php", "size": 1024, "checksum": { "algorithm": "sha256", "digest": "…" }, "is_symlink": false, "symlink_target": null }
    ],
    "signature": { "algorithm": "sha256", "digest": "…" }
}

It never contains secrets β€” only which algorithms were used.

Feature tour

Databases

MySQL, MariaDB, PostgreSQL and SQLite. Choose connections, exclude tables, or keep only a table's structure:

'databases' => [
    'connections' => ['mysql', 'reporting'], // [] = the default connection
    'exclude_tables' => ['telescope_entries'],
    'schema_only_tables' => ['sessions', 'cache'], // structure kept, rows skipped
],

Passwords are passed through the environment, never the command line. Details & the PostgreSQL client-version note: docs/databases.md.

Files

Multiple source roots, prefix/glob excludes, hidden-file and symlink handling:

'files' => [
    'include' => [base_path('app'), base_path('.env'), storage_path('app/public')],
    'exclude' => [base_path('vendor'), '*.log'],
    'follow_symlinks' => false, // record link metadata instead of following
    'include_hidden' => true,
],

Details: docs/files.md.

Compression

none, gzip, bzip2 or zstd, streamed so memory stays flat:

'compression' => ['algorithm' => 'gzip', 'level' => 6],

Encryption & integrity

Streaming AES-256 (CBC or authenticated GCM), SHA-256/512 checksums, and optional HMAC signing of the manifest:

'encryption' => [
    'algorithm' => 'aes-256-gcm', // none | aes-256-cbc | aes-256-gcm
    'key' => env('BACKUP_ENCRYPTION_KEY'), // 32 bytes, base64: accepted
],
'integrity' => [
    'checksum' => 'sha256',
    'signing' => ['enabled' => true, 'key' => env('BACKUP_SIGNING_KEY')],
],

CBC streams (any size); GCM is authenticated but buffered in memory and capped by encryption.gcm_max_megabytes. Full guidance: docs/security.md.

Storage destinations

Any Laravel disk β€” write to several at once:

'destinations' => ['s3', 'local'],

Local, S3-compatible, FTP, SFTP, Azure Blob, Backblaze B2, WebDAV. Setup examples: docs/storage.md.

Restore (with rollback)

php artisan backup:restore                    # newest, everything
php artisan backup:restore --database         # databases only
php artisan backup:restore --files            # files only
php artisan backup:restore --only=/var/www/app/config   # selected directories

Restores verify the archive against its manifest first. By default they take a pre-restore snapshot and roll everything back if the restore fails β€” files are written via a temporary file and atomic rename. Opt out with --unsafe. Details: docs/restore.md.

Retention

Grandfather-Father-Son or a simple count, plus age and total-size caps:

'retention' => [
    'strategy' => 'gfs',
    'gfs' => ['keep_daily' => 7, 'keep_weekly' => 4, 'keep_monthly' => 6, 'keep_yearly' => 2],
    'max_age_days' => 365,
    'max_storage_megabytes' => null,
],
php artisan backup:clean --dry-run   # preview

Details: docs/retention.md.

Scheduling & queues

Enable the built-in schedule (registered with withoutOverlapping) and/or run backups on the queue:

'schedule' => ['enabled' => true, 'backup' => '0 2 * * *', 'cleanup' => '0 4 * * *', 'monitor' => '0 6 * * *'],
'queue' => ['enabled' => true, 'queue' => 'backups', 'timeout' => 3600],

With the queue enabled, backup:run dispatches a RunBackupJob; force a synchronous run with --sync. Details: docs/scheduling.md.

Monitoring, history & notifications

backup:monitor reports per-destination health and exits non-zero when unhealthy (so it doubles as an uptime check). Every operation is logged and, if the migration is published, stored in a backup_history table. Failures and unhealthy destinations can notify you by mail and/or Slack:

'monitoring' => [
    'max_backup_age_hours' => 25,
    'store_history' => true,
    'notifications' => [
        'enabled' => true,
        'on' => ['failure', 'unhealthy'],
        'mail' => ['to' => ['ops@acme.test']],
        'slack' => ['webhook_url' => env('BACKUP_NOTIFY_SLACK')],
    ],
],

CLI reference

Command Purpose
backup:run Create a backup (--profile, --sync)
backup:list List stored backups (--profile, --disk)
backup:verify Verify a backup against its manifest
backup:restore Restore (--database, --files, --only, --no-verify, --unsafe, --force)
backup:clean Apply retention (--dry-run, --disk)
backup:monitor Report backup health

Full flags and examples: docs/cli.md.

Programmatic API

use Nyoncode\BackupManager\Facades\Backup;
use Nyoncode\BackupManager\Contracts\Storage\BackupRepository;
use Nyoncode\BackupManager\Managers\RestoreManager;
use Nyoncode\BackupManager\DTOs\RestoreOptions;

// Create
$result = Backup::run();                 // or Backup::run('nightly')
$result->archiveFileName;                // nightly-2026-07-16-020000-a1b2c3.tar.gz.enc
$result->manifest->fileCount();

// Restore
$backup = app(BackupRepository::class)->list('s3', 'backups')[0]; // newest
app(RestoreManager::class)->restore($backup, RestoreOptions::databasesOnly());

The managers return typed value objects (BackupResult, RestoreResult, HealthReport, StoredBackup, Manifest) β€” ideal for a UI layer.

Architecture

A backup is a pipeline of small stages threaded through a shared BackupContext:

PrepareWorkspace β†’ DumpDatabases β†’ CollectSourceFiles β†’ BuildArchive
    β†’ CompressArchive β†’ EncryptArchive β†’ BuildManifest β†’ StoreBackup β†’ CleanupWorkspace

Each stage implements BackupStage and is injected into BackupManager, so you can add, remove or reorder stages without touching the core. Every capability sits behind an interface (DatabaseDumper, Compressor, Encryptor, Signer, Archiver, BackupRepository, RetentionStrategy) with a focused implementation. See docs/extending.md.

Testing

composer test              # Unit + Feature (no database server needed)
composer analyse           # PHPStan level 8
composer test:integration  # real backup ↔ restore against live services

The integration suite runs real backup β†’ restore round-trips against live MySQL, MariaDB, PostgreSQL and MinIO (S3). Point it at your own servers with the BM_* environment variables (see tests/Integration/DatabaseTestKit.php for the keys and defaults); any service that isn't reachable is skipped automatically, so composer test stays green without them.

CI (.github/workflows/tests.yml) runs:

  • static-analysis β€” PHPStan level 8
  • tests β€” Unit + Feature across the full support grid: Laravel 12 on PHP 8.3 / 8.4 / 8.5 and Laravel 13 on PHP 8.4 / 8.5, plus a lowest-dependencies leg
  • windows β€” the platform-agnostic Unit suite on windows-latest
  • integration β€” a matrix leg per engine (MySQL 8.4, MariaDB 11, PostgreSQL 16, MinIO/S3), each against its own real service container

Documentation

Topic English Česky
Overview & index docs/README.md docs/cz/README.md
Installation installation.md instalace.md
Configuration configuration.md konfigurace.md
Databases databases.md databaze.md
Files files.md soubory.md
Storage storage.md uloziste.md
Security & manifest security.md zabezpeceni.md
Restore restore.md obnova.md
Retention retention.md retence.md
Scheduling & monitoring scheduling.md planovani.md
CLI reference cli.md cli.md
Extending extending.md rozsireni.md

Roadmap

Where the package could go after 1.0 (incremental backups, restorability testing, a fully streamed pipeline and more): ROADMAP.md.

License

The MIT License (MIT). See LICENSE.