TL;DR: How to quickly create a backup of a Laravel project and store it on Dropbox using Spatie Laravel-backup and Flysystem-dropbox packages.
A sample Laravel project can be found on this Github Repository. Find out more on Capsules or X.
In web project management, performing regular backups is essential to prevent permanent data loss. Various types of data require backups, including databases, user-generated files, or even files directly related to the web project itself.
Project files are often already managed by a version control system like Github or Gitlab. However, it’s generally not recommended to store user data there. Dropbox can be an excellent choice for securely storing this type of data.
Before transferring backups to Dropbox, it’s useful to generate them first. Spatie’s laravel-backup
package makes this task much simpler. Use the following command to install it :
composer require spatie/laravel-backup
Next, simply create the backup
configuration file to quickly set up the project backup:
config/backup.php
<?php
return [
'backup' => [
'name' => 'capsules',
'source' => [
'files' => [
'include' => [ base_path() ],
'exclude' => [ base_path( 'vendor' ), base_path( 'node_modules' ) ],
'relative_path' => null
],
'databases' => [ 'sqlite' ]
],
'database_dump_filename_base' => 'database',
'database_dump_file_extension' => '',
'destination' => [
'compression_method' => ZipArchive::CM_DEFAULT,
'compression_level' => 9,
'filename_prefix' => "backup-",
'disks' => [ 'local' ]
],
'temporary_directory' => '',
'password' => null,
'encryption' => 'default',
'tries' => 1,
'retry_delay' => 0,
]
];
To start the backup :
php artisan backup:run
Starting backup...
Dumping database /dropbox/database/database.sqlite...
Determining files to backup...
Zipping 110 files and directories...
Created zip containing 110 files and directories. Size is 112.76 KB
Copying zip to disk named local...
Successfully copied zip to disk named local.
Backup completed!
The php artisan backup:run
command creates a compressed backup that includes the items specified in the include
key while excluding those listed in exclude
. It also adds the specified databases
and stores everything in the folder defined by the name
key, naming the file with the filename_prefix
followed by the backup date in the format YYY-MM-DD-HH-mm-ss
. This backup is saved in the location set by the disks
key, which in this case is local
and redirects to storage/app/private.
Additional configuration elements, not mentioned here above, are nonetheless essential for the proper functioning of the package.
storage
> app
> private
> capsules
- backup-2024-10-29-08-30-00.zip
- backup-2024-10-28-08-30-00.zip
...
With the backup procedure now operational, the next step is to link it to your Dropbox account. For this, we once again turn to Spatie and its flysystem-dropbox
package. As the name suggests, this is the Flysystem adapter for the Dropbox V2 API.
composer require spatie/flysystem-dropbox
A new configuration needs to be added to the filesystems
configuration file.
config/filesystems.php
<?php
return [
'disks' => [
...
'dropbox' => [
'driver' => 'dropbox',
'token_url' => env( 'DROPBOX_TOKEN_URL' ),
'refresh_token' => env( 'DROPBOX_REFRESH_TOKEN' )
]
]
];
Laravel backup
also needs to be linked to the new dropbox
disk. This is a good opportunity to adjust some additional settings.
<?php
return [
'backup' => [
...
'name' => '',
...
'destination' => [
...
'filename_prefix' => Illuminate\Support\Str::replace( '.', '-', env( 'DROPBOX_APP_NAME' ) ) . '-',
'disks' => [ 'dropbox' ]
],
...
]
];
- The
name
key is left empty to avoid duplicating the folder name ith the Dropbox app, and thefilename_prefix
now uses theDROPBOX_APP_NAME
environment variable instead ofbackup-
.
It is now time to configure the Dropbox integration. The following steps detail the Dropbox setup process. Be aware that some steps may be overly complex.
A. Create a Dropbox Application :
- Go to : https://www.dropbox.com/developers/apps
- Click on
Create app
- Choose an API :
Scoped access
- Select the access type :
App folder
- Name the app :
Capsules Codes Article
- Go to the Permissions tab and check the following options : [ files.metadata.write, files.metadata.read, files.content.write, files.content.read ]
- Click on
Submit
B. Authorize Access :
- Grant access to the
Capsules Codes Article
app by modifying<YOUR_APP_KEY>
in the following url. - Continue and Authorize.
https://www.dropbox.com/oauth2/authorize?client_id=<YOUR_APP_KEY>&response_type=code&token_access_type=offline
C. Generate the Required Token :
- Obtain the
refresh_token
by replacing<ACCESS_CODE>
with the previously generated access code, along with<APP_KEY>
and<APP_SECRET>
found in the settings of the newly created app :
curl https://api.dropbox.com/oauth2/token -d code=<ACCESS_CODE> -d grant_type=authorization_code -u <APP_KEY>:<APP_SECRET>
A response in the following format will appear, and a folder will be
created in the Dropbox interface :
{"access_token": "sl.B48oJpRyl-FP61gr56g1GUa4G26L3TAQasMbiRRyuAGHpUQXz0hyeA4OrO8Zes6clhQxhA8dyFurNcx4_gwPXJb_K3ZZOfdzBIVwHieInx-EEFNgIc6ZOR3VMRjoOQ-Ske_QjGMePmzS", "token_type": "bearer", "expires_in": 14399, "refresh_token": "PKnAG5FX-ngAAAAAAAAAAVaXknd9i45Xk6t4-FrmXGjR4jWEljmBALOF3Xexp4AY", "scope": "account_info.read files.content.read files.content.write files.metadata.read files.metadata.write", "uid": "682879329", "account_id": "dbid:AADQ-j2-ihak-i0IwMiVS8F5-KhkcyTHyg8"}
The refresh_token
should be added to the application’s environment variables, along with the app_key
and app_secret
located in the settings of the newly created Dropbox application. These can then be added to the .env
file.
.env.example
DROPBOX_APP_NAME="Capsules Codes Article"
DROPBOX_APP_KEY=
DROPBOX_APP_SECRET=
DROPBOX_REFRESH_TOKEN=
DROPBOX_TOKEN_URL=https://${DROPBOX_APP_KEY}:${DROPBOX_APP_SECRET}@api.dropbox.com/oauth2/token
The final step is to create the service provider that will establish the connection to the Dropbox service : DropboxServiceProvider
.
app\Providers\DropboxServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
use GuzzleHttp\Client;
class DropboxServiceProvider extends ServiceProvider
{
public function boot(): void
{
Storage::extend( 'dropbox', function( Application $app, array $config )
{
$resource = ( new Client() )->post( $config[ 'token_url' ] , [ 'form_params' => [ 'grant_type' => 'refresh_token', 'refresh_token' => $config[ 'refresh_token' ] ] ]);
$accessToken = json_decode( $resource->getBody(), true )[ 'access_token' ];
$adapter = new DropboxAdapter( new DropboxClient( $accessToken ) );
return new FilesystemAdapter( new Filesystem( $adapter, $config ), $adapter, $config );
});
}
}
Do not forget to add it to the providers.php
.
bootstrap/providers.php
<?php
return [ App\Providers\DropboxServiceProvider::class ];
All that is left is to add the daily backup scheduling commands in the console.php
file.
routes/console.php
<?php
use Illuminate\Support\Facades\Schedule;
Schedule::command( 'backup:run' )->daily();
Schedule::command( 'backup:clean' )->daily();
- This code allows the various commands to run once a day using the
daily
method, provided that a scheduler has been configured for the application. Without this daily command, therefresh_token
will expire.
> php artisan backup:run
Starting backup...
Dumping database /dropbox/database/database.sqlite...
Determining files to backup...
Zipping 35 files and directories...
Created zip containing 35 files and directories. Size is 36.74 KB
Copying zip to disk named dropbox...
Successfully copied zip to disk named dropbox.
Backup completed!
> php artisan backup:clean
Starting cleanup...
Cleaning backups of on disk dropbox...
Used storage after cleanup: 36.74 KB.
Cleanup completed!
Glad this helped.