error `Missing storage symlink` for voyager admin panel in cPanel

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Fixing the Missing storage symlink Error for Voyager on cPanel

Deploying a Laravel application, especially one utilizing file storage like Voyager, often introduces subtle configuration hurdles when moving from a local development environment to a live server like cPanel. One of the most frustrating errors developers encounter is the cryptic message: Missing storage symlink, which prevents images and files from being displayed in the admin panel.

If you are running a setup where image uploads fail or new user avatars do not show up, you've likely hit this wall. While running php artisan storage:link fixes this instantly on your local machine, the question becomes: How do we replicate this fix in a live cPanel environment?

As senior developers, we need solutions that work reliably outside of the local terminal. Let’s dive deep into why this happens and how to solve it permanently for your production application.


Understanding the Root Cause: Symlinks and Web Access

The error Missing storage symlink stems from how Laravel's file system drivers handle public access to stored files. When you upload a file through an HTTP request, the web server needs a direct, accessible path to that storage location.

In Laravel, the default setup uses symbolic links (symlinks) within the public directory to map the application's internal storage directory to the web-accessible public/storage path. This allows the framework to serve files correctly via URLs like /storage/image.jpg.

When you use drivers like 'local', Laravel expects this symlink to exist so that when Voyager tries to load an image, it can find the file mapped correctly in the public view space. On a standard local setup, running php artisan storage:link creates this necessary link perfectly.

However, on shared hosting environments or cPanel setups, there are often strict limitations regarding file system operations and permissions that prevent the Artisan command from executing successfully, leading to the live server error.

Addressing the Configuration Shift

You mentioned you have already adjusted your configuration in config/filesystems.php, changing the driver root from storage_path() to public_path(). This is a crucial step for making storage accessible publicly, but it shifts the responsibility of creating the public link onto the deployment process itself.

Here is how we approach fixing this issue when command-line execution is restricted:

1. Verify Filesystem Configuration

Ensure your configuration reflects the correct setup. Based on your description, the structure should look something like this in config/filesystems.php:

'disks' => [
    'local' => [
        'driver' => 'local',
        // Ensure this path points to where you want storage located relative to public
        'root' => public_path('app/public'), 
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],
    // ... other disks
],

The key is setting the root correctly and ensuring that whatever system command or deployment script runs afterwards, it has the necessary permissions to create the required symlink between your application’s storage directory and the public view directory.

2. The Live Server Solution: Manual Symlink Creation (If Possible)

Since running Artisan commands might be blocked on cPanel, the most reliable solution is often a manual intervention or a deployment script that executes the necessary shell command outside of the restricted web environment, or by using SSH access directly to the server.

If you have SSH access:
Log into your server via SSH and navigate to your application's root directory. Execute the command directly:

php artisan storage:link

This bypasses any potential execution restrictions imposed by the web interface and forces the creation of the necessary symlink between storage/app/public and public/storage. This is the most direct way to resolve the issue if permissions are the only blocker.

3. Best Practice: Deployment Scripts

For robust, repeatable deployments (which is a core principle in modern Laravel development), avoid relying on manual steps. Instead, integrate this step into your deployment process using tools like Git hooks or custom deployment scripts (e.g., using Deployer or simple shell scripts). This ensures that every deployment automatically recreates the necessary file structure and symlinks, preventing these kinds of post-deployment errors.

Conclusion

The Missing storage symlink error is a common symptom of an environment mismatch between local development and live deployment, specifically concerning file system linking. While the fix on a local machine is trivial via Artisan, solving it on cPanel requires understanding server permissions. By verifying your filesystems.php configuration and ensuring the necessary symbolic link exists—ideally through direct SSH execution or automated deployment scripts—you can ensure that your Voyager admin panel functions perfectly on production.

For further insights into building robust and scalable applications with Laravel, always refer to the official documentation and best practices available at laravelcompany.com.