laravel-cloud-db-dumper maintained by vitisstudio
Laravel Cloud DB Dumper
Pull a Laravel Cloud database down to your machine with one command.
php artisan db:pull resolves your Cloud target — organization, application, environment, database —
fetches the connection credentials for it, dumps it to a file, and optionally restores it into your local
database and runs a seeder to scrub what you just pulled down. It only asks about the parts it cannot
work out for itself.
Dumps are kept, so a later run can restore an earlier snapshot without downloading anything, and
--prune clears them out again. Where policy forbids production data at rest, --no-store keeps the
dump out of your project entirely.
php artisan db:pull
Application: acme-web
┌ Environment ─────────────────────────────────────────────────┐
│ › production │
│ staging │
└──────────────────────────────────────────────────────────────┘
Dump written to: database/backups/forge_pgsql_2026-09-24.sql
Local database restored.
Install it as a dev dependency
composer require --dev vitisstudio/laravel-cloud-db-dumper
This package belongs in require-dev and nowhere else. It exists to move production data onto a
developer workstation: it reads your Laravel Cloud API tokens, fetches live database credentials, and
overwrites your local database. None of that should be reachable from a deployed application. Installing
it into require ships that capability to production for no benefit.
The service provider is auto-discovered, so there is nothing to register.
Publishing the config file is optional — the defaults work:
php artisan vendor:publish --tag="cloud-db-dumper-config"
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.3 |
| Laravel | 11, 12 or 13 |
| Laravel Cloud CLI | >= 0.5, authenticated |
| Database client tools | pg_dump + psql, or mysqldump + mysql |
The Cloud CLI does the authentication, so this package never asks you for a token:
composer global require laravel/cloud-cli
cloud auth
Usage
php artisan db:pull
The command walks you through it:
- Pick a target. Organization, application, environment, then database. Anything that can be worked out is not asked about — see How the target is resolved.
- Choose where dumps land. Defaults to
database/backups, and is skipped when dump storage is off. - Reuse or refetch. Every dump already on disk for that database is offered, newest first, so you can restore an earlier snapshot instead of downloading anything.
- Restore locally. Opt in, after an explicit warning naming the local database about to be overwritten. Active connections to it are terminated first so the restore is not blocked.
- Seed. Optionally run one of your seeders against the restored data — the place to scrub emails, tokens and anything else that should not sit on a laptop.
Your choices are remembered, so the next run is a single confirmation.
Arguments and options
Name the application and environment the way you would with any cloud command — an ID or a name,
either one:
php artisan db:pull acme-web staging
php artisan db:pull app-9f3c env-2a71
| Argument / option | Effect |
|---|---|
application |
Application ID or name; skips the application prompt |
environment |
Environment ID or name; skips the environment prompt |
--organization= |
Run against a named Cloud organization (details) |
--fresh |
Ignore saved preferences and pick the database again |
--download |
Always fetch a fresh dump, ignoring the ones already on disk (details) |
--no-store |
Never leave the dump on disk (details) |
--no-restore |
Dump only; leave the local database untouched |
--no-seed |
Skip the post-restore seeder step |
--prune |
Delete the stored dumps and exit (details) |
--force |
Skip the prune confirmation, for scripts |
php artisan db:pull staging --no-seed
Naming an application or environment overrides the saved target, so you never have to answer "use the saved one?" with "no" first.
How the target is resolved
db:pull resolves each part the way the Cloud CLI's own commands do, and only asks when something is
genuinely ambiguous:
| Part | Resolution order |
|---|---|
| Organization | --organization → CLOUD_ORGANIZATION → saved preference → .cloud/config.json → prompt |
| Application | argument → .cloud/config.json → the app deployed from your git remote → sole → prompt |
| Environment | argument → .cloud/config.json → sole → prompt, defaulting to the app's default env |
| Database | sole → prompt, defaulting to the one the environment is wired to |
If you have already run cloud repo:config in the project, db:pull inherits those defaults and can
run without a single prompt. Anything resolved for you is echoed, so a quiet run still tells you what
it picked.
The environment is deliberately not inferred from your current git branch, unlike cloud deploy.
This command overwrites your local database, so which environment it reads from stays an explicit
choice.
Multiple Cloud organizations
Cloud CLI 0.5 holds one API token per organization, and it only prompts you to choose between them when
it is attached to a terminal — which it never is when a package shells out to it. Left alone, it fails
with Multiple API tokens found.
This package resolves the organization itself: it asks you once, then forwards the matching token for the rest of the run. The organization name is remembered with your other preferences. Only the name — the token is never written to disk.
To skip that prompt entirely, name the organization up front:
php artisan db:pull --organization="Acme Inc"
CLOUD_ORGANIZATION="Acme Inc"
Configuration
| Key | Env | Default | Purpose |
|---|---|---|---|
cloud_binary |
CLOUD_BINARY |
cloud |
Path to the Cloud CLI, if it is not on your PATH |
organization |
CLOUD_ORGANIZATION |
null |
Pin the Cloud organization |
backup_path |
— | database/backups |
Where dumps are written |
store_dumps |
CLOUD_DB_DUMPER_STORE_DUMPS |
true |
Whether dumps are kept on disk at all |
prefs_file |
— | .db-backup-prefs.json |
Where the last target is remembered |
binaries |
see below | null (discover on PATH) |
Absolute paths to the database client binaries |
Point the package at binaries that live outside your PATH — a DBngin install, for instance:
PG_DUMP_PATH="/Users/Shared/DBngin/postgresql/17.2/bin/pg_dump"
PSQL_PATH="/Users/Shared/DBngin/postgresql/17.2/bin/psql"
MYSQLDUMP_PATH=
MYSQL_PATH=
What gets written to your project
| Path | Contents |
|---|---|
database/backups/*.sql |
Dumps, named {database}_{driver}_{Y-m-d}.sql |
.db-backup-prefs.json |
The last target you picked, plus your default seeder |
Both belong in your .gitignore:
/database/backups
.db-backup-prefs.json
Database credentials are fetched live from Laravel Cloud on every run and held in memory only. They are never written to the preferences file, the dump filename, or console output.
Restoring an earlier dump
Dumps accumulate under backup_path, one per database per day, and every one of them stays restorable.
On a repeat pull you are shown what is already there:
┌ 3 local dumps of acme_production already exist. Use one? ─────┐
│ Download a fresh dump │
│ › 2026-09-24 (12.4 MB) — today │
│ 2026-09-20 (12.1 MB) │
│ 2026-06-25 (9.8 MB) │
└───────────────────────────────────────────────────────────────┘
Picking one skips the download entirely — no Cloud credentials are fetched — and restores that file. Today's dump is preselected, since that is what a repeat run usually wants; an older snapshot is always a deliberate choice. Only dumps of the same database and driver are listed.
Use --download to skip the question and always pull afresh:
php artisan db:pull --download
Dumps are never deleted behind your back. Clear them out with --prune when you want the space back.
Deleting stored dumps
php artisan db:pull --prune
┌─────────────────┬────────┬────────────┬────────┐
│ Database │ Driver │ Taken │ Size │
├─────────────────┼────────┼────────────┼────────┤
│ acme_production │ pgsql │ 2026-09-24 │ 2.0 KB │
│ acme_production │ pgsql │ 2026-09-20 │ 4.0 KB │
│ acme_staging │ mysql │ 2026-06-25 │ 1.0 KB │
└─────────────────┴────────┴────────────┴────────┘
Deleting 3 dumps (7.0 KB) from /app/database/backups. This cannot be undone.
┌ Delete these dumps? ────────────────────────────┐
│ Yes / No │
└─────────────────────────────────────────────────┘
Every file is listed with its size and the day it was taken before anything happens, and the confirmation defaults to no. Pruning never contacts Laravel Cloud — it is a local file operation, so there is no application picker to walk first.
Only dumps this package wrote are ever deleted. Files are matched against the
{database}_{driver}_{date}.sql naming scheme, so anything else living in that folder is invisible to
prune and cannot be removed by it, even by accident.
For scripts, --force skips the confirmation:
php artisan db:pull --prune --force
Without --force, a non-interactive run declines and deletes nothing.
Keeping nothing on disk
A dump is production data sitting on a laptop. Where a data handling policy does not allow that, turn storage off and the dump never lands in your project:
CLOUD_DB_DUMPER_STORE_DUMPS=false
php artisan db:pull --no-store
The dump is written to a private temporary directory created at mode 0700, restored into your local
database, and deleted before the command exits — including when the restore fails. Same-day caching is
off in this mode, because there is no longer a file to reuse, so every run downloads afresh.
Setting it in config covers the whole team; --no-store covers a single run. --no-store together with
--no-restore is refused, since that combination would download a dump and then delete it unused.
The preferences file is a separate thing and is still written. It records the application, environment,
cluster and database names you picked, and never any credentials — delete it, or point prefs_file
somewhere outside the repository, if even that is more than your policy allows.
Contributing
composer test # Pest
composer analyse # PHPStan / Larastan, level 5
composer format # Pint
Pull requests are welcome. Please keep the test suite and PHPStan green.
Security
Please review our security policy for how to report a vulnerability. Do not open a public issue for security problems.
Changelog
See CHANGELOG.md for what has changed recently.
Credits
Built on spatie/db-dumper and spatie/laravel-package-tools.
License
The MIT License (MIT). Please see LICENSE.md for more information.