DEV Community

Cover image for Create a Backup of a Laravel Project to Nextcloud
Capsules Codes
Capsules Codes

Posted on • Originally published at capsules.codes

Create a Backup of a Laravel Project to Nextcloud

TL;DR: How to quickly create a backup of your Laravel project and store it on Nextcloud.

 
 

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
Enter fullscreen mode Exit fullscreen mode

 
 

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' )

    ]
];
Enter fullscreen mode Exit fullscreen mode

 
 

To trigger the backup :

 
 

php artisan backup:run
Enter fullscreen mode Exit fullscreen mode

 
 

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!
Enter fullscreen mode Exit fullscreen mode

 
 

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
                ... 
Enter fullscreen mode Exit fullscreen mode

 
 

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
Enter fullscreen mode Exit fullscreen mode

 
 

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

          ],

          ...
    ]
];     
Enter fullscreen mode Exit fullscreen mode

 
 

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' ]
        ],

                ...
    ]
];
Enter fullscreen mode Exit fullscreen mode

 
 

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}/
Enter fullscreen mode Exit fullscreen mode
  • 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,
  }
Enter fullscreen mode Exit fullscreen mode

 
 

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 );
        } );
    }
}
Enter fullscreen mode Exit fullscreen mode

 
 

Don't forget to register the service provider in providers.php :

 
 

bootstrap/providers.php

<?php

return [

    App\Providers\AppServiceProvider::class,
    App\Providers\NextcloudServiceProvider::class

];
Enter fullscreen mode Exit fullscreen mode

 
 

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();
Enter fullscreen mode Exit fullscreen mode
  • 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!
Enter fullscreen mode Exit fullscreen mode

 
 

Nextcloud Image 001

 
 

> php artisan backup:clean

Starting cleanup...
Cleaning backups of Article on disk nextcloud...
Used storage after cleanup: 23.99 KB.
Cleanup completed!
Enter fullscreen mode Exit fullscreen mode

 
 

Glad this helped.

Top comments (0)