Sail: troubleshooting & performance
This guide collects frequent Sail friction that is not really âLaravel bugsâ: filesystem quirks, Compose networking, image caches, and PHP extensions behaving differently in containers. Use it together with Databases & services, Queues & workers, and Environments & deployment.
Navigation: All tools ¡ Sail overview ¡ Databases ¡ Queues ¡ Env & deploy
Table of contents
- WSL2, Docker Desktop, and file sync
- Permissions,
storage, andvendor - Port already allocated /
FORWARD_* - âIt works on the host but not in Sailâ
- Stale code: rebuilds and layers
- OPcache and autoload in local dev
- Xdebug feels slow
- Vite, npm, and Node inside vs outside Sail
- Logs and quick diagnostics
- When to reset volumes (data loss)
WSL2, Docker Desktop, and file sync
On Windows + WSL2, bind-mounting from /mnt/c/... into Linux containers is often slow and can confuse watchers (Vite, php artisan serve file polling). Prefer keeping the project inside the WSL filesystem (e.g. ~/projects/...) and running Sail from there.
If editors on Windows still touch files across the boundary, expect occasional permission or timestamp odditiesânormalize on one side (all edits in WSL, or all in Windows with a clear rule).
Permissions, storage, and vendor
Laravel needs storage/ and bootstrap/cache/ writable by the PHP user in laravel.test. On Linux hosts, UID/GID mapping via WWWUSER / WWWGROUP in .env (see Sail docs) reduces âroot-owned filesâ after sail artisan runs.
Quick checks inside the app container:
sail exec laravel.test ls -la storage bootstrap/cache
If logs fail to write, fix ownership or permissions inside the container, not only on the host copyâbind mounts reflect both directions.
Port already allocated / FORWARD_*
Sail publishes MySQL, Redis, app HTTP, etc. to localhost. Another local MySQL or a second Sail project often causes address already in use.
Set distinct values per project in .env:
FORWARD_DB_PORT=3307
APP_PORT=8081
FORWARD_REDIS_PORT=6380
Then sail down && sail up -d. Inside containers, internal ports (3306, 6379) stay the same; only the host mapping changes. See Environments & deployment.
âIt works on the host but not in Sailâ
Remember: php / composer on your Mac use host PHP; sail artisan uses the container PHP and extensions. Missing pdo_pgsql, redis, or mongodb in the image is a classic mismatchâfix by editing the published Dockerfile and sail build --no-cache.
Database hosts in .env must be Compose service names (pgsql, redis), not 127.0.0.1, because the app runs inside laravel.test. See Databases & services.
Stale code: rebuilds and layers
After changing Dockerfile steps (pecl install, apt packages, PHP version base image), a normal sail up may still use cached layers. Use:
sail build --no-cache
sail up -d
After pulling a new composer.lock, run sail composer install so vendor/ matches the containerâs PHP version.
Long-running queue workers cache the app bootstrap; use sail artisan queue:restart after code changes (Queues).
OPcache and autoload in local dev
Production PHP images often enable OPcache with aggressive settings. If file changes do not seem to apply, check opcache.revalidate_freq / validate_timestamps in php.ini inside the container (Sail publishes overrides in some setups). For local iteration, ensuring timestamps are validated avoids âphantom old codeâ.
Composer optimized autoload (--optimize-autoloader) in dev is optional; if class not found errors persist after PSR-4 moves, run sail composer dump-autoload.
Xdebug feels slow
Xdebug always adds overhead. Disable it when you do not need breakpoints (Sail supports toggling per their docs / env flags for your version). Prefer targeted debugging sessions instead of leaving Xdebug on all day.
Vite, npm, and Node inside vs outside Sail
Teams split responsibilities:
- Run
npm run devon the host when the Vite server binds to a port you open in the browser. - Or use Sailâs Node service /
sail npmwhen everything should stay in Compose.
Mixing them is fine if APP_URL, HMR host, and proxied ports line up. Misaligned URLs usually show as websocket / HMR failed or 404 on @viteâalign ports with Env & deploy.
Logs and quick diagnostics
sail logs -f laravel.test
docker compose ps
sail exec laravel.test php -v
sail exec laravel.test php -m
For database connectivity from the app container: sail exec laravel.test php artisan migrate:status (or a trivial tinker connection test). For Redis: sail exec redis redis-cli ping.
When to reset volumes (data loss)
sail down -v removes named volumesâall local DB/Redis data for that project is gone. Use it when you suspect corrupted volume state or after major image upgradesânot as a daily habit.
To remove a single volume, use docker volume ls and docker volume rm <name> so other projects are unaffected. See Databases & services.