Shared hosting
This was built with cheap shared hosting as the default target, not an afterthought:
- Redis is optional everywhere. Cache and queue both run fine on the
databasedriver if Redis isn't available — the installer's requirements page just reports whether Redis is present, it never requires it. - Background jobs run inline by default, on purpose. Emails and notifications (invoice sent, payment received, low stock, and others) do implement Laravel's queueable contract, but the default
QUEUE_CONNECTION=syncruns them inline during the same request rather than deferring them — no worker process to keep alive, which matters on shared hosting. If you're on a VPS and want these to send in the background instead, switch toQUEUE_CONNECTION=databaseand run a worker — see Running a queue worker in the installation guide. No Redis or Horizon required either way. - No
php artisan storage:linkneeded. Nothing in the app serves files from the public disk/symlink — uploaded tenant logos are streamed through a controller route from the private disk instead, specifically because some hosts disable PHP'ssymlink()function. - PDF generation is pure PHP (dompdf), not a headless Chromium — no extra binary or server dependency to install.
Common issues
A page shows raw/old CSS or a blank screen after pulling an update. Run npm run build (or npm run dev while actively developing) — compiled frontend assets aren't regenerated automatically just by changing PHP files or pulling new code.
"419 Page Expired" right after install. Usually a session/cookie mismatch after the installer rewrote your .env (e.g. SESSION_DRIVER changed from file to database once you configured a database). Clear your browser cookies for the site and try again; if it persists, confirm APP_URL in .env matches the actual URL you're visiting (including https:// if you're on SSL).
License verification fails or times out during install. The install wizard degrades gracefully here — if the license server is unreachable, you'll see a clear error on that step, but nothing about your data/database is affected by it.
What happens after install — is the license ever re-checked? Yes, weekly, via the license:verify scheduled command (requires the cron entry in Running scheduled tasks) — see the License step in Installation for how the grace period works if it's ever rejected.
I enabled HR / added a tax rate / invited a teammate and don't see it. Double check you're logged in as a role that has the relevant permission (see Roles and permissions) — most of the app hides rather than errors when you lack access to something, which can look like "it didn't work" if you're on a restricted role.
A Sales Rep says they can't see a deal/lead that definitely exists. This is almost always correct behavior, not a bug — Sales Rep only sees leads/deals assigned to them. Reassign it, or view it as a Sales Manager/Admin/Owner.
Error tracking (optional)
Nothing is bundled here — this app logs through Laravel's standard config/logging.php channels (files, by default) and nothing more, to avoid forcing a buyer into a specific vendor. If you want automated exception aggregation/alerting instead of tailing log files, most error-tracking services (Sentry, Bugsnag, Flare, and others) provide a Laravel package that adds itself as another logging channel — install the one you prefer, add it to the channels array in config/logging.php, and set it as (or add it to) your stack channel. No code changes needed beyond that.
Where things live, if you need to dig in
- Tenant/platform state:
tenantsandplatform_settingstables (the latter is a deliberate single-row table,id = 1). - Roles/permissions: standard
spatie/laravel-permissiontables, in "teams" mode keyed bytenant_id. - Every tenant-owned table has a
tenant_idcolumn and is automatically scoped to the current tenant — this is enforced by a global query scope covered by an automated test suite, not something to work around.
← All Coravo – AI CRM & ERP Software for Growing Businesses documentation