Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ciarlill's avatar

Deployments and the 'storage' directory

I want to preface by saying I know there are multiple ways this could be accomplished. This is more or less for me to get feedback on my initial idea and maybe get suggestions for more elegant solutions, or be made aware of potential issues I am not seeing.

I am working on an ansible playbook for deploying our laravel based applications. The deployment model is pretty standard: dated release directories with a symlink from the "current" deployment to the latest release. The one major difference is that on my servers I separate all apache/www-user write-able directories from the application sources. This makes managing permissions and ACLs much easier for us. So an example directory structure would be:

/srv/www/data/my-app/(storage|uploads)
/srv/www/sites/my-app/(current|releases)

Everything under /data is read-write for www-user. Everything under /sites is read-only for www-user.

For my WordPress sites, the only directory that needs to be write-able is /uploads, so I just create the directory under the /data tree and symlink to it on each deploy. However, with Laravel, the storage directory is 1) part of the repo, 2) has sub-directory structure that may be subject to change, 3) should probably be cleared out on each deploy to clear up compiled templates?

  1. This is easy enough to fix. Just remove the storage directory from our repos, and setup our deployment to recreate the directory structure in our /data area.
  2. If the storage directory structure changes with later versions of laravel, our deployment process must be expanded to handle deploying different versions of laravel. I would like to avoid this.
  3. Should all the data really be cleared out on each deployment? This would wipe out all user sessions as well would it not? Should user sessions be cleared on deploy anyway - probably? Is everything under /storage considered transient?

So my deployment would look something like:

  • Fetch latest release from git repo (with /storage removed from repo)
  • rm -rf /srv/www/data/my-app/storage
  • re-create storage directory structure under /srv/www/data/my-app/
  • symlink storage directory to latest release
  • setup release symlink

Pretty straightforward with the exception of the caveats listed above. This would also not allow roll-backs to include whatever was in the /storage directory at the time of deployment, but if everything under /storage is transient this should not matter.

I would also repeat all of this for the /app/bootstrap/cache directory. It also annoys me to no-end that this write-able directory is in that location. I feel it should be under /app/storage/bootstrap. But I am a little neurotic about keeping all write-able things in one area.

So if you made it this far - thanks for reading! I look forward to any suggestions from the community or examples of how you all are handling this.

Thanks

0 likes
2 replies
fideloper's avatar

Sounds like you're on the right track. Here's some things you can try out as well.

Storage Dir

A common way to handle the storage directory (and this is how Envoyer does it I believe) is not to delete and re-create the storage directory, but instead to keep re-using a single one. This lets you persist logs and cache in one location without worrying about wiping them out.

You might have the storage dir in /var/www/storage. Then when you deploy, you can:

# Delete emtpy-ish storage dir from code repo
rm -rf /var/www/my-app/storage

# Symlink existing storage dir from real location into "my-app"
ln -sf /var/www/storage /var/www/my-app/storage

Bootstrap dir

The writable "stuff" in the bootstrap dir are all "optional," meaning any caches/files in there can actually safely be deleted and doesn't need to persist between deployments. Files in there will either be re-created after the first request into the app, or can be re-created by adding/re-running certain tasks on deploy, such as:

php artisan optimize
php artisan config:cache
php artisan route:cache

More info on those above three commands are available on this free mini-course here: https://serversforhackers.com/laravel-perf

OstapBrehin's avatar

If you use livewire, you will also need this command for deployment:

php artisan livewire:discover

to avoid bootstrap/cache directory is not present or not writeable error

Please or to participate in this conversation.