hasinhayder / tyro-checkpoint
Database checkpoint management for Laravel local development
Requires
- php: ^8.1
- illuminate/console: ^10.0 || ^11.0 || ^12.0 || ^13.0
- illuminate/database: ^10.0 || ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^10.0 || ^11.0 || ^12.0 || ^13.0
- symfony/process: ^6.0 || ^7.0 || ^8.0
Requires (Dev)
- laravel/pint: ^1.29
- orchestra/testbench: ^8.0 || ^9.0 || ^10.0 || ^11.0
- phpunit/phpunit: ^10.0 || ^11.0
README
Database checkpoints for Laravel local development
Tyro Checkpoint is a simple Laravel package that provides Git-like checkpoint functionality for your database during local development. Supports SQLite, MySQL, and PostgreSQL. Create snapshots of your database state and restore them instantly when needed.
Features
- Create full database snapshots with a single command
- List all available checkpoints with metadata
- Restore any checkpoint to reset your database state
- Delete old checkpoints to save disk space
- Lock checkpoints to prevent accidental deletion
- Add notes to checkpoints for better organization
- Encryption support to secure your database snapshots
- Auto-checkpoints before risky Artisan commands like migrations and seeders
- Supports SQLite, MySQL, and PostgreSQL
- Simple and production-safe
- No configuration required
Requirements
- PHP 8.1 or higher
- Laravel 10.x, 11.x, 12.x and 13.x
- SQLite, MySQL 8+, or PostgreSQL 12+
- MySQL:
mysqldumpandmysqlCLI tools (installmysql-client) - PostgreSQL:
pg_dumpandpsqlCLI tools (installpostgresql-client)
Installation
Install the package via Composer:
composer require hasinhayder/tyro-checkpoint --dev
The package will automatically register itself via Laravel's package discovery.
Run the installation command to setup everything:
php artisan tyro-checkpoint:install
This will:
- Check your database configuration
- Validate required CLI binaries for MySQL/PostgreSQL
- Create the checkpoint storage directory
- Create the checkpoints metadata file (checkpoints.json)
- Optionally create an initial checkpoint
That's it! You're ready to create checkpoints.
Note: No database migrations are needed as checkpoint metadata is stored in a JSON file.
Optional: Encryption
If you want to secure your checkpoints, you can generate an encryption key:
php artisan tyro-checkpoint:generate-key
This will add a secure TYRO_CHECKPOINT_ENCRYPTION_KEY to your .env file. If a key already exists, the command will warn you before replacing it.
Optional: Publish Configuration
Publish the configuration file to customize settings:
php artisan tyro-checkpoint:publish-config
This creates config/tyro-checkpoint.php where you can customize the storage path.
Usage
Auto-Checkpoints Before Risky Commands
Enable auto-checkpoints to create a safety snapshot before risky Artisan commands run:
TYRO_CHECKPOINT_AUTO_ENABLED=true
By default, Tyro Checkpoint watches these commands:
migrate
migrate:fresh
migrate:refresh
migrate:reset
migrate:rollback
db:seed
db:wipe
When enabled, running a risky command creates a checkpoint automatically:
php artisan migrate:fresh --seed
Example output:
✓ Auto checkpoint created before migrate:fresh: auto_2026_06_17_153000_migrate_fresh
Create a Checkpoint
Create a checkpoint with an auto-generated name:
php artisan tyro-checkpoint:create
Create a checkpoint with a custom name:
php artisan tyro-checkpoint:create my_feature_before_changes
Create an encrypted checkpoint:
php artisan tyro-checkpoint:create secure_snapshot --encrypt
Create a checkpoint without interactive note prompts, useful for cron jobs and scripts:
php artisan tyro-checkpoint:create nightly_backup --silent
Example output:
Creating checkpoint...
✓ Checkpoint created successfully!
ID: 1
Name: my_feature_before_changes
Size: 2.45 MB
Created: 2026-02-01 10:30:15
List All Checkpoints
View all available checkpoints:
php artisan tyro-checkpoint:list
Example output:
Found 3 checkpoint(s):
+----+---------------------------+---------+---------------------+--------+-----------+-------+
| ID | Name | Size | Created At | Locked | Encrypted | Note |
+----+---------------------------+---------+---------------------+--------+-----------+-------+
| 3 | before_user_migration | 2.48 MB | 2026-02-01 14:20:00 | No | No | |
| 2 | after_seeding | 2.45 MB | 2026-02-01 12:15:30 | Yes | Yes | Clean |
| 1 | clean_database | 1.98 MB | 2026-02-01 10:00:00 | No | No | |
+----+---------------------------+---------+---------------------+--------+-----------+-------+
View Checkpoint Details
Inspect a checkpoint by ID or name:
php artisan tyro-checkpoint:details 1 php artisan tyro-checkpoint:details clean_database
You can also use tyro-checkpoint:list with an ID or name as a shortcut:
php artisan tyro-checkpoint:list 1 php artisan tyro-checkpoint:list clean_database
If no identifier is provided, you can choose from the checkpoint list:
php artisan tyro-checkpoint:details
Restore a Checkpoint
Restore a checkpoint by ID:
php artisan tyro-checkpoint:restore 1
Or restore by name:
php artisan tyro-checkpoint:restore clean_database
You'll be asked to confirm before the restore happens. If no identifier is provided, you can choose from a list:
Available checkpoints:
+----+---------------------------+---------+---------------------+-----------+-------+
| ID | Name | Size | Created At | Encrypted | Note |
+----+---------------------------+---------+---------------------+-----------+-------+
| 2 | after_seeding | 2.45 MB | 2026-02-01 12:15:30 | Yes | Clean |
| 1 | clean_database | 1.98 MB | 2026-02-01 10:00:00 | No | |
+----+---------------------------+---------+---------------------+-----------+-------+
Select a checkpoint to restore (enter 0 to quit):
[0] Quit
[1] #2 - after_seeding (encrypted) (Note: Clean)
[2] #1 - clean_database
>
Important: Checkpoints are NOT deleted after restoration. You can restore the same checkpoint multiple times, allowing you to experiment with different approaches and always return to the same state.
Add a Note to a Checkpoint
Add a descriptive note to help you remember what a checkpoint represents:
php artisan tyro-checkpoint:add-note 1
You'll be prompted to enter your note:
Enter note for checkpoint #1 (or press Enter to remove existing note):
> Clean state with seeded users
The note will be displayed when you list checkpoints.
Encrypt an Existing Checkpoint
Encrypt an existing unencrypted checkpoint in place. This secures the snapshot without creating a new checkpoint entry:
php artisan tyro-checkpoint:encrypt 1 php artisan tyro-checkpoint:encrypt clean_database
If no identifier is provided, only the unencrypted checkpoints are listed and you can pick one by ID:
Unencrypted checkpoints:
+----+---------------------------+---------+---------------------+--------+
| ID | Name | Size | Created At | Note |
+----+---------------------------+---------+---------------------+--------+
| 1 | clean_database | 1.98 MB | 2026-02-01 10:00:00 | |
+----+---------------------------+---------+---------------------+--------+
Enter checkpoint ID to encrypt (0 to quit):
After encryption, the original unencrypted snapshot is removed and the checkpoint metadata is updated so it stays restorable (restore auto-decrypts encrypted checkpoints). No new entry is added to the list.
Idempotent: running
tyro-checkpoint:encrypton an already-encrypted checkpoint is a no-op — it will not double-encrypt, since re-encrypting ciphertext would make the checkpoint unrestorable.
Note: An encryption key is required. Generate one first with
php artisan tyro-checkpoint:generate-key.
Lock/Unlock a Checkpoint
Lock a checkpoint to prevent accidental deletion:
php artisan tyro-checkpoint:lock 1
Unlock a checkpoint to allow deletion:
php artisan tyro-checkpoint:unlock 1
Locked checkpoints cannot be deleted individually or via the flush command. This is useful for preserving important baseline states.
Flag/Unflag a Checkpoint
Flag a checkpoint to mark it for attention:
php artisan tyro-checkpoint:flag 1
Unflag a checkpoint when it no longer needs attention:
php artisan tyro-checkpoint:unflag 1
If no identifier is provided, both commands ask for the checkpoint ID or name. Flagged checkpoints show a 🚩 marker in checkpoint tables.
Delete a Checkpoint
Delete a checkpoint by ID:
php artisan tyro-checkpoint:delete 1
Or delete by name:
php artisan tyro-checkpoint:delete clean_database
You'll be asked to confirm before deletion. Note that locked checkpoints cannot be deleted:
Checkpoint to delete:
ID: 2
Name: after_seeding
Size: 2.45 MB
Created: 2026-02-01 12:15:30
Note: Clean state
Are you sure you want to delete this checkpoint? (yes/no) [no]:
Delete All Checkpoints
Delete all unlocked checkpoints at once with the flush command:
php artisan tyro-checkpoint:flush
Locked checkpoints are preserved. You'll see a list of checkpoints to be deleted:
⚠ WARNING: This will delete ALL unlocked checkpoints permanently!
Checkpoints to be deleted:
+----+---------------------------+---------+---------------------+--------+
| ID | Name | Size | Created At | Locked |
+----+---------------------------+---------+---------------------+--------+
| 3 | before_user_migration | 2.48 MB | 2026-02-01 14:20:00 | No |
| 1 | clean_database | 1.98 MB | 2026-02-01 10:00:00 | No |
+----+---------------------------+---------+---------------------+--------+
Total: 2 checkpoint(s) (1 locked checkpoint will be preserved)
Are you sure you want to delete ALL unlocked checkpoints? (yes/no) [no]:
Skip the confirmation prompt with the --force flag:
php artisan tyro-checkpoint:flush --force
How It Works
- Checkpoints are full snapshots: Each checkpoint is a complete copy of your database (SQLite: direct file copy; MySQL/PostgreSQL: SQL dump)
- Stored locally: Checkpoint files are stored in
storage/tyro-checkpoints/ - Metadata tracking: Checkpoint metadata (ID, name, path, size, created_at, locked, note, driver) is stored in
storage/tyro-checkpoints/checkpoints.json- outside the database to prevent loss when restoring - Restore process: Restoring a checkpoint replaces your current database with the selected checkpoint (drop & import for MySQL/PostgreSQL; file replacement for SQLite)
- Persistent checkpoints: Checkpoints are NOT automatically deleted after restoration. You can restore the same checkpoint multiple times and must manually delete checkpoints when no longer needed.
- Safe restoration: Because metadata is stored outside the database, you never lose track of any checkpoint, even when restoring to an earlier state
- Lock protection: Locked checkpoints are protected from deletion to preserve important baseline states
- Automatic Decryption: If a checkpoint is encrypted, the restore command will automatically decrypt it using your
TYRO_CHECKPOINT_ENCRYPTION_KEYbefore replacing your database.
Common Use Cases
Protect Important Baselines with Lock
# Create and lock a clean baseline php artisan tyro-checkpoint:create clean_baseline php artisan tyro-checkpoint:lock clean_baseline php artisan tyro-checkpoint:add-note clean_baseline # Enter: "Fresh install with migrations and seeders" # Now you can safely flush all other checkpoints php artisan tyro-checkpoint:flush # Your locked baseline is preserved! # Restore to baseline anytime php artisan tyro-checkpoint:restore clean_baseline
Before Running Migrations
php artisan tyro-checkpoint:create before_migration php artisan migrate # If something goes wrong: php artisan tyro-checkpoint:restore before_migration # The checkpoint is still available for future restores
Testing with Fresh Data
php artisan tyro-checkpoint:create clean_state php artisan db:seed # Test your application php artisan tyro-checkpoint:restore clean_state # Test again with fresh data - checkpoint is preserved php artisan tyro-checkpoint:restore clean_state # Can restore as many times as needed
Experimenting with Database Changes
php artisan tyro-checkpoint:create before_experiment # Make manual database changes # Test your changes php artisan tyro-checkpoint:restore before_experiment # Try a different approach php artisan tyro-checkpoint:restore before_experiment # Try yet another approach - same checkpoint, multiple restores
Additional Commands
Check Version and Status
Display package version and system information:
php artisan tyro-checkpoint:version
This shows:
- Package version and history
- Laravel and PHP versions
- Documentation and GitHub links
Installation Command
Re-run the installation setup:
php artisan tyro-checkpoint:install
Useful when:
- Setting up the package on a new environment
- Verifying your SQLite configuration
- Creating the checkpoint storage directory
Publish Configuration
Publish the configuration file:
php artisan tyro-checkpoint:publish-config
This creates config/tyro-checkpoint.php where you can customize:
- Checkpoint storage path
Storage Location
Checkpoint files and metadata are stored at:
storage/tyro-checkpoints/
├── checkpoints.json # Metadata for all checkpoints
├── checkpoint_name_1.sqlite # Database snapshot 1
├── checkpoint_name_2.sqlite # Database snapshot 2
└── ...
- checkpoints.json: Contains metadata (ID, name, path, size, created_at, locked, note) for all checkpoints
- {checkpoint_name}.sqlite: The actual database snapshot files
Important: The metadata is stored in a JSON file (not in the database) so that restoring a checkpoint doesn't cause you to lose track of other checkpoints.
Configuration
Publish the configuration file to customize settings:
php artisan tyro-checkpoint:publish-config
The published configuration file (config/tyro-checkpoint.php) allows you to customize:
return [ // Custom storage path for checkpoints 'storage_path' => storage_path('tyro-checkpoints'), // Encryption key (automatically set via artisan tyro-checkpoint:generate-key) 'encryption_key' => env('TYRO_CHECKPOINT_ENCRYPTION_KEY'), // Process timeout (seconds) for snapshot/restore operations (mysqldump, pg_dump, ...) 'process' => [ 'timeout' => env('TYRO_CHECKPOINT_PROCESS_TIMEOUT', 600), ], // Auto checkpoints before risky Artisan commands 'auto_checkpoint' => [ 'enabled' => env('TYRO_CHECKPOINT_AUTO_ENABLED', false), 'commands' => [ 'migrate', 'migrate:fresh', 'migrate:refresh', 'migrate:reset', 'migrate:rollback', 'db:seed', 'db:wipe', ], 'name_prefix' => env('TYRO_CHECKPOINT_AUTO_NAME_PREFIX', 'auto'), 'encrypt' => env('TYRO_CHECKPOINT_AUTO_ENCRYPT', false), 'stop_on_failure' => env('TYRO_CHECKPOINT_AUTO_STOP_ON_FAILURE', true), ], ];
Important Notes
- Local development only: This package should only be used in local development environments. Install it as a dev dependency with
--dev. - Not for production: Never use this package in production environments.
- In-memory databases not supported: SQLite
:memory:databases cannot be checkpointed. - CLI binaries required: MySQL and PostgreSQL drivers require native CLI tools (
mysqldump/mysql,pg_dump/psql). - Metadata stored outside database: Checkpoint metadata is stored in a JSON file, not in the database itself. This ensures you never lose track of checkpoints when restoring.
- Checkpoints persist after restore: Checkpoints are NOT automatically deleted when restored. This allows you to restore the same checkpoint multiple times to test different approaches.
- Manual cleanup required: Use
php artisan tyro-checkpoint:deleteorphp artisan tyro-checkpoint:flushto remove checkpoints you no longer need. - Disk space: Each checkpoint is a full copy of your database, so they can consume disk space. Delete old checkpoints you no longer need.
- Locked checkpoints: Locked checkpoints are protected from deletion. Use this to preserve important baseline states.
- Encryption: Encrypting checkpoints secures the database snapshots. Ensure you back up your
TYRO_CHECKPOINT_ENCRYPTION_KEY, as losing it will make all encrypted checkpoints impossible to restore. Replacing a key will also invalidate older encrypted checkpoints.
Error Handling
The package includes comprehensive error handling:
- Unsupported database driver: Error if your database driver is not SQLite, MySQL, or PostgreSQL
- Missing CLI binary: Error if MySQL/PostgreSQL CLI tools are not installed
- In-memory database: Error if using
:memory:SQLite database - Missing database file: Error if the database file doesn't exist
- Duplicate name: Error if creating a checkpoint with an existing name
- Missing checkpoint: Error if trying to restore/delete a non-existent checkpoint
- Locked checkpoint: Error if trying to delete a locked checkpoint
- File operations: Error if checkpoint file operations fail
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This package is open-sourced software licensed under the MIT license.
Credits
Created by Hasin Hayder
Support
If you encounter any issues or have questions, please open an issue on GitHub.