Why would a Laravel 13 app deployed from an image that dropped empty folders fail every page with 'Please provide a valid cache path', and what is missing?
answer
- Git keeps empty folders with .gitignore
- realpath() of a missing folder is false
- view.compiled from storage/framework/views
- bootstrap/cache must be present and writable
- LARAVEL_STORAGE_PATH or useStoragePath()
basics
~10 sstorage/framework/views is missing. The view config sets the compiled path to realpath(storage_path('framework/views')), which is false for a missing folder, so the Blade compiler throws. Recreate the storage and bootstrap/cache tree, writable by PHP.
solid answer
~40 sThe skeleton keeps its runtime folders in Git only through placeholder `.gitignore` files: `storage/app/{private,public}`, `storage/framework/{cache,sessions,testing,views}`, `storage/logs` and `bootstrap/cache`. A build that copies only tracked files, excludes `storage/`, or strips dot-files can ship without them. The view config's `compiled` path is `realpath(storage_path('framework/views'))`, and `realpath()` returns `false` for a folder that does not exist, so the Blade compiler's constructor throws `InvalidArgumentException: Please provide a valid cache path.` The sibling error, `The .../bootstrap/cache directory must be present and writable.`, comes from writing the package manifest. Fix the image, not the error: create the full tree, make `storage/` and `bootstrap/cache` writable by the PHP user, or relocate storage with `LARAVEL_STORAGE_PATH` or `useStoragePath()`.
code
bash · 6 lines# In the image build or container entrypoint
mkdir -p storage/app/private storage/app/public \
storage/framework/cache/data storage/framework/sessions \
storage/framework/testing storage/framework/views \
storage/logs bootstrap/cache
chown -R www-data:www-data storage bootstrap/cachego deeper
Remember that storage/ and bootstrap/cache must exist and be writable, and that Git keeps them only through placeholder files.
Explain how the compiled view path is derived from storage/framework/views and why a missing folder yields a cache-path error.
Make builds and containers create the runtime tree, set ownership narrowly, and relocate storage deliberately when volumes or read-only filesystems demand it.
Standardise the runtime-directory contract for every Laravel image the organisation builds, so no service rediscovers this failure.
## The symptom A school-timetable app runs fine locally. After a new container image or archive-based deploy, every page returns a 500 error, and the log (if it can be written at all) shows: - `InvalidArgumentException: Please provide a valid cache path.`, or - `Exception: The /srv/timetable/bootstrap/cache directory must be present and writable.` Both point at **runtime folders that the deploy did not create**. ## Why the folders can disappear Git does not track empty directories. The skeleton keeps its runtime folders alive with **placeholder `.gitignore` files** that ignore everything except themselves: | Folder | Kept by | Holds at runtime | |---|---|---| | `storage/app/private`, `storage/app/public` | `.gitignore` in each | Files your code stores | | `storage/framework/cache/data` | `.gitignore` in `cache` | File cache store | | `storage/framework/sessions` | `.gitignore` | File sessions | | `storage/framework/views` | `.gitignore` | Compiled Blade templates | | `storage/framework/testing` | `.gitignore` | Test-run files | | `storage/logs` | `.gitignore` | `laravel.log` | | `bootstrap/cache` | `.gitignore` | Package and services manifests, optimize caches | Builds lose them in predictable ways: 1. a Docker ignore rule or rsync exclude for `storage/` "because it is runtime data"; 2. a packaging step that skips dot-files, taking the placeholders with it; 3. a volume mounted over `storage/` that starts empty. ## Why the error says "cache path" The framework's view config sets: - `compiled` to `env('VIEW_COMPILED_PATH', realpath(storage_path('framework/views')))`. PHP's `realpath()` returns `false` when the folder does not exist. The Blade compiler's constructor checks its cache path and throws `Please provide a valid cache path.` when it is empty. So the message is about **compiled views**, not about the cache store. The `bootstrap/cache` message comes from the package manifest writer (and the provider repository): before writing `packages.php` or `services.php` it checks that the folder is writable and throws if not. ## Fixing it properly - Create the whole tree in the image build, for example with `mkdir -p` for each folder in the table, and keep the placeholders out of any ignore rules. - Make `storage/` and `bootstrap/cache` writable by the user PHP runs as; everything else can stay read-only. - If storage must live elsewhere (a shared volume, a read-only root filesystem), relocate it deliberately: set the `LARAVEL_STORAGE_PATH` environment variable or call `useStoragePath()` on the application, and point `VIEW_COMPILED_PATH` somewhere writable if needed. - Recreate the subfolders on an empty mounted volume at container start, before the app serves traffic. ## Preventing it in CI - Add a smoke step after building the image that runs a command needing compiled views, or simply requests the home page inside the container, so a missing folder fails the build rather than production. - Keep a checked-in script (or Dockerfile `RUN` line) that creates the tree, so nobody relies on the placeholders surviving every packaging tool. - Treat any new ignore rule touching `storage/` or `bootstrap/` as a review item. - When storage lives on a mounted volume, create the subfolders in the container entrypoint, because a fresh volume starts empty regardless of what the image contained. ## Diagnosing quickly - `ls -la storage/framework bootstrap/cache` inside the running container. - Check the user: a folder owned by root with mode 755 is present but not writable for a PHP process running as another user. - If the log file cannot be written either (for example `storage/` is not writable by the PHP user), read the web server's or PHP's own error log instead. ## Common misreadings - "Clear the cache and it goes away": there is nothing to clear; the folder does not exist. - "The file cache store is broken": the message comes from the Blade compiler. - "Commit the compiled views": they are generated per environment and are ignored by design.
- How can a Laravel 13 app keep its storage directory outside the code tree?Set the `LARAVEL_STORAGE_PATH` environment variable, which `storagePath()` reads when no path was set in code, or call `useStoragePath('/mnt/timetable-storage')` on the application in `bootstrap/app.php`. The new location still needs the `app`, `framework` and `logs` subfolders, and `VIEW_COMPILED_PATH` can move compiled views on its own.
- Why can the error appear even when storage/framework/views exists?The compiled path comes from `realpath()`, which also returns `false` when the PHP user cannot resolve the path, for example because a parent directory is not searchable. A folder that exists but is not writable fails later, when a compiled template is written. Check ownership and permissions for the PHP user, not just existence.
saying these in an interview costs you the question
- The message means the Redis or file cache store is misconfigured
- Running cache:clear recreates missing storage folders
- storage/ should be excluded from images because it is runtime data
- Compiled Blade views should be committed to Git
- Making the whole project writable is the right fix