Mastering Laravel Deployments: Shared Storage and Seamless Updates
Ron Chaplin
Deploying a Laravel application to a Virtual Private Server (VPS) is an exciting step. It’s when your app leaves the safety of your local machine and takes on the real world. But deployment comes with its own set of challenges—like ensuring user-uploaded files don’t disappear when you push a new version. In this post, I’ll guide you through configuring a Laravel 12 app for a multi-release deployment on a VPS, complete with shared storage for persistent files and a script to automate the process. Let’s dive in and make deployment something you’ll actually enjoy.
The Scenario: A VPS Deployment with a Twist
Picture this: you’ve got a VPS ready to host your Laravel app. You’re using a deployment setup where each new version lives in its own folder—something like /home/user/app-name/releases/release-id—and a symlink at /home/user/app-name/current points to the active release. This approach lets you switch releases with zero downtime. Awesome, right? But here’s the catch: when users upload files (think profile pics or PDFs), those files get stored in the release-specific storage directory. Switch to a new release, and poof—they’re gone.
Our mission is to keep those uploaded files safe across deployments while keeping each release tidy and isolated. Let’s break it down.
Step 1: Configuring Laravel for Shared Storage
Let’s tackle the biggie: keeping user-uploaded files persistent. By default, Laravel tucks these into storage/app/public, but since each release has its own storage folder, those files don’t follow the current symlink to the new release. We need a shared spot they can call home.
Here’s how to set it up:
- Create a shared directory outside the release folders for uploads.
mkdir -p /home/user/app-name/shared/uploads
- Update Laravel’s filesystem config in
config/filesystems.phpto point thepublicdisk to this shared directory.
'disks' => [
'public' => [
'driver' => 'local',
'root' => '/home/user/app-name/shared/uploads',
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
],
],
- Link it up. Normally, you’d run php artisan storage:link to symlink public/storage to storage/app/public. With our updated config, that symlink now points to the shared uploads folder instead.
This setup ensures that no matter which release is active, your app always looks to the same shared spot for user files. It’s like giving those files a permanent address while the app itself keeps moving house.
Step 2: Adjusting the Deployment Script
Manual deployments are fine for a one-time thing, but let’s automate this to save time and avoid slip-ups. A good deployment script should handle creating the shared directory, deploying the new release, linking resources, and switching the symlink—all in one go.
Here’s a sample script to get you started:
#!/bin/bash
APP_USER="user"
WEB_GROUP="www-data"
SHARED_DIR="/home/user/app-name/shared"
CURRENT_SYMLINK="/home/user/app-name/current"
NEW_RELEASE="/home/user/app-name/releases/$(date +%Y%m%d%H%M%S)"
GIT_URL="https://github.com/yourusername/your-repo.git"
LATEST_TAG="main"
# Set up shared uploads directory
mkdir -p "$SHARED_DIR/uploads"
chown $APP_USER:$WEB_GROUP "$SHARED_DIR/uploads"
chmod 775 "$SHARED_DIR/uploads"
# Deploy the new release
git clone --branch "$LATEST_TAG" --depth 1 $GIT_URL "$NEW_RELEASE"
# Link shared resources (like .env)
ln -s "$SHARED_DIR/.env" "$NEW_RELEASE/.env"
# Set up the release
cd "$NEW_RELEASE"
composer install --no-dev --optimize-autoloader
php artisan storage:link
php artisan migrate --force
npm install
npm run build
# Switch to the new release
ln -sfn "$NEW_RELEASE" "$CURRENT_SYMLINK"
Run this with bash deploy.sh, and you’ve got a fresh release live with all the shared bits in place. Automation like this turns deployment into a single command instead of a checklist.
Conclusion: Deploy with Confidence
There you have it—a Laravel deployment strategy that keeps user files safe and updates smooth. By using a shared directory for uploads and a script to handle the heavy lifting, you’re set to push new releases without breaking a sweat.
Key takeaways:
- A shared directory keeps uploaded files persistent across releases.
- A deployment script ties it all together for quick, reliable updates.
Deploying is as much a learning curve as it is a technical task. Figuring out how to keep files intact across releases teaches you to think beyond the code to the whole system. Now, go forth and deploy—your app’s ready for the spotlight! Got your own deployment tips? I’d love to hear them.