artisan as www-data, reload PHP-FPM after every code change, rebuild the caches after every code or .env change, and rebuild the front-end after CSS or Blade class changes. Backups run every night at 01:30 and keep the newest 14 dumps; copy them off the server yourself. The commands below use erp as the Docker Compose service name from Self-hosting with Docker.
Four rules
1. Run artisan as www-data, never as root
1. Run artisan as www-data, never as root
docker exec runs as root by default. Files root creates in storage/ or bootstrap/cache/ cannot be rewritten by PHP-FPM, and random pages then fail with touch(): Utime failed: Operation not permitted.2. Reload PHP-FPM after a code change
2. Reload PHP-FPM after a code change
PHP opcache runs with
validate_timestamps = 0, so PHP never re-reads a file it has compiled, Blade views included. A change does nothing until PHP-FPM reloads. A graceful reload that drops no requests:3. Keep the caches warm
3. Keep the caches warm
.env does nothing until optimize runs again. Clear them with optimize:clear and filament:optimize-clear.4. The front-end is compiled
4. The front-end is compiled
The public pages and the panel theme are built by Vite into
public/build/ with npm run build. Run it after changing CSS or Tailwind classes in Blade files. The home page’s icon font is subset by name: a new Material Symbol must be added to the icon_names= list in resources/views/landing/index.blade.php, or it shows as its name.Logs
Application errors go tostorage/logs/laravel.log. Count the distinct errors instead of scrolling:
Scheduled jobs
The scheduler (schedule:work, run by Supervisor) runs these every day:
The queue worker (
queue:work) builds company data exports and posts stock valuation entries when the stock ledger is on. If those stop, check it under Supervisor before anything else.
Backups and restore
saas:backup writes storage/app/backups/<database>_<timestamp>.sql.gz, checks it, and keeps the newest 14 (SAAS_BACKUPS_KEEP). The platform operator’s home page shows the age of the last one and turns red after 26 hours; set SAAS_OPS_EMAIL with working mail to be told when a run fails.
These dumps sit on the same server as the database, so copy them elsewhere every night (for example with rsync from another machine). To restore one:
Debugging
A quick health check:
/up, /, /admin/login and /saas/register should all answer 200.