Blog / Create a Backup of a Laravel Project on Nextcloud
4 min - 29 Jul 2025
TL;DR: How to quickly create a backup of your Laravel project and store it on Nextcloud using Spatie Laravel-backup and Flysystem-webdav packages.
You can find a sample Laravel project on this Github Repository. Learn more on Capsules, X or Bluesky.
Several months ago, a similar article was written, but that one focused on automatically backing up to Dropbox.
However, a few weeks ago, while searching for a more suitable personal file storage system, a new solution emerged: Nextcloud.
Nextcloud is an open-source file hosting software and collaborative platform. It can be accessed via WebDAV, any web browser, or specialized clients. Its open architecture allows for the extension of its features.
In other words, Nextcloud is an excellent candidate for storing backups. This article assumes the reader has access to a Nextcloud instance on a dedicated server.
Before transferring backups to Nextcloud, we first need to generate them. The spatie/laravel-backup
package simplifies this process. Install it with the following command:
composer require spatie/laravel-backup
Then create the backup
config file to quickly configure project backups :
config/backup.php
<?php
use Illuminate\Support\Str;
return [
'backup' => [
'name' => 'capsules',
'source' => [
'files' => [
'include' => [ base_path( 'public' ), base_path( 'storage' ) ],
'exclude' => [],
'relative_path' => base_path(),
],
'databases' => [ 'sqlite' ]
],
'destination' => [
'filename_prefix' => 'backup-',
'disks' => [ 'local' ]
],
'temporary_directory' => storage_path( 'app' )
]
];
To trigger the backup :
php artisan backup:run
Starting backup...
Dumping database /nextcloud/database/database.sqlite...
Determining files to backup...
Zipping 37 files and directories...
Created zip containing 37 files and directories. Size is 32.11 KB
Copying zip to disk named local...
Successfully copied zip to disk named local.
Backup completed!
The command php artisan backup:run
creates a compressed backup containing what's listed under the include
key, excluding anything in exclude
. It includes databases and stores the result in a folder named after the value in the name
key, naming the file with the filename_prefix
followed by the timestamp YYYY-MM-DD-HH-mm-ss
. The final zip is placed in the disk location defined under the disks
key, here set to local
, which corresponds to storage/app/private
.
storage
> app
> private
> capsules
- backup-2025-07-25-14-15-41.zip
- backup-2025-07-25-14-15-51.zip
...
The backup procedure is now operational. It must now be linked to the Nextcloud account. Since Nextcloud uses the WebDAV protocol, we’ll need a PHP League package: flysystem-webdav
, which is a Flysystem adapter for WebDAV.
composer require league/flysystem-webdav
Add a new disk configuration in the filesystems.php
file:
config/filesystems.php
<?php
return [
'disks' => [
'nextcloud' => [
'driver' => 'nextcloud',
'url' => env( 'NEXTCLOUD_URL' ),
'prefix' => env( 'NEXTCLOUD_PREFIX' ),
'username' => env( 'NEXTCLOUD_USERNAME' ),
'password' => env( 'NEXTCLOUD_PASSWORD' ),
'auth' => 1
],
...
]
];
Now update laravel-backup
to use the nextcloud
disk and tweak a few additional settings.
config/backup.php
<?php
return [
'backup' => [
...
'name' => env( 'NEXTCLOUD_APP_NAME', '' ),
...
'destination' => [
...
'filename_prefix' => Str::replace( '.', '-', env( 'NEXTCLOUD_APP_NAME' ) ) . '-',
'disks' => [ 'nextcloud' ]
],
...
]
];
Add environment variables to .env.example
and .env
:
.env.example
NEXTCLOUD_URL=
NEXTCLOUD_USERNAME=
NEXTCLOUD_PASSWORD=
NEXTCLOUD_APP_NAME=Article
NEXTCLOUD_PREFIX=/cloud/remote.php/dav/files/${NEXTCLOUD_USERNAME}/
cloud
prefix. Normally, it would be via remote.php/dav/files/${NEXTCLOUD_USERNAME}
.To ensure credentials are correct and Nextcloud is accessible, use these two lines in Laravel Tinker via php artisan tinker
:
> $config = Config::get("filesystems.disks.nextcloud");
= [
"driver" => "nextcloud",
"url" => "<https://capsules.codes>",
"prefix" => "/cloud/remote.php/dav/files/username@capsules.codes/Applications/",
"username" => "username@capsules.codes",
"password" => "password",
"auth" => 1,
]
> Process::run('curl -s -o /dev/null -w "%{http_code}" -u ' . $config["username"] . ':' . $config["password"] . ' -X PROPFIND "' . $config["url"] . $config["prefix"] . '" -H "Depth: 1"');
= Illuminate\Process\ProcessResult {#5355
output: "207",
errorOutput: "",
exitCode: 0,
successful: true,
}
Now create a service provider to establish the connection to Nextcloud : NextcloudServiceProvider
.
app\Providers\NextcloudServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Storage;
use Illuminate\Contracts\Foundation\Application;
use Sabre\DAV\Client;
use League\Flysystem\WebDAV\WebDAVAdapter;
use Illuminate\Filesystem\FilesystemAdapter;
use League\Flysystem\Filesystem;
class NextcloudServiceProvider extends ServiceProvider
{
public function boot(): void
{
Storage::extend( 'nextcloud', function( Application $app, array $config )
{
$client = new Client( [ 'baseUri' => $config[ 'url' ], 'userName' => $config[ 'username' ], 'password' => $config[ 'password' ], 'authType' => $config[ 'auth' ] ] );
$adapter = new WebDAVAdapter( $client, $config[ 'prefix' ] );
return new FilesystemAdapter( new Filesystem( $adapter, $config ), $adapter, $config );
} );
}
}
Don't forget to register the service provider in providers.php
:
bootstrap/providers.php
<?php
return [
App\Providers\AppServiceProvider::class,
App\Providers\NextcloudServiceProvider::class
];
Set up daily scheduled backup commands in console.php
.
routes/console.php
<?php
use Illuminate\Support\Facades\Schedule;
Schedule::command( 'backup:run' )->daily();
Schedule::command( 'backup:clean' )->daily();
daily
method — assuming a scheduler is properly configured.> php artisan backup:run
Starting backup...
Dumping database /capsulescodes/database/database.sqlite...
Determining files to backup...
Zipping 27 files and directories...
Created zip containing 27 files and directories. Size is 23.99 KB
Copying zip to disk named nextcloud...
Successfully copied zip to disk named nextcloud.
Backup completed!
> php artisan backup:clean
Starting cleanup...
Cleaning backups of Article on disk nextcloud...
Used storage after cleanup: 23.99 KB.
Cleanup completed!
Glad this helped.