Blog / Create a Backup of a Laravel Project on Nextcloud

Image used for article Create a Backup of a Laravel Project on Nextcloud

Create a Backup of a Laravel Project on Nextcloud




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 CapsulesX 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}/
  • Since the Nextcloud file manager is hosted alongside other tools, access is done via the 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();
  • This code will run the commands once a day using the 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.

v1.5.4

X IconBluesky IconGithub Icon