Updates & rollback
How to update a production install safely — backup first, in-app updater or manual files, composer, migrations, cache and Filament commands, queue restart, verification and rollback.
Updating a live CRM touches code, the database, caches and long-running workers. The order below is the production-safe order: backup → update code → dependencies → migrate → caches → workers → verify, with a rollback plan in hand before you start.
Who this is for
- Super-admin / hosting admin — applying releases.
- Developer — preparing a release or recovering from a bad one.
Prerequisites: Backups & restore understood, cron running, a maintenance window.
Step 0 — Backup first (non-negotiable)
- Super Admin → Backups → Create backup (or
php artisan leadhub:backup). - Verify it (the smoke-test action) and download it off-site.
- If your host offers one-click full-account backups, take one of those too — code + files + DB in one.
No backup, no update Migrations are generally additive, but a rollback that needs data back is impossible without a dump. Two minutes of backup beats a day of incident response.
Step 1 — Check what you're updating to
- In-app: the update screen checks
LEADHUB_UPDATE_SERVERand compares againstLEADHUB_VERSION. - Terminal:
php artisan leadhub:check-updates.
Read the release notes for the version span (especially: new migrations, changed Filament pages, new env keys — new .env.example keys are the usual cause of post-update warnings).
Step 2 — Put the app into maintenance (optional but kind)
php artisan down --secret="your-bypass-token"
You can keep working with https://…/your-bypass-token; visitors see a maintenance page. Skip this only if the release is explicitly zero-downtime.
Step 3 — Apply the update
Route A — In-app updater (recommended on shared hosting)
Run the update from the Super Admin update screen. The updater applies the release zip and then automatically:
- clears compiled views/routes/config,
- republishes Filament assets (
filament:assets) — important because zip updates skip Composer'spost-autoload-dump, so the framework'sfilament:upgradenever fired during the copy, - refreshes
vendor/composerclassmap metadata (classmap-authoritative installs) and resets opcache where the host allows.
Route B — Manual (file upload or git)
cd ~/crm-app
# 1. backup already taken in Step 0
# 2a. upload the release over the existing files (File Manager/FTP), or:
# 2b. git pull (if you deploy from a repository)
# 3. dependencies (only if Terminal available):
composer install --no-dev --optimize-autoloader
Never overwrite .env or storage/ Release zips contain .env.example and the storage/ skeleton — never let an extraction clobber your live .env, storage/app/* or your public/storage symlink. Extract carefully or use the in-app updater.
The platform also ships a cache healer: after a file-copy update it detects a changed shipped version (LEADHUB_VERSION in config/leadhub.php vs the stored version stamp) and deletes stale compiled config/route/event caches on the next boot. Manual cache steps below remain the belt-and-braces.
Step 4 — Run migrations
php artisan migrate --force
--force is required in production (non-interactive). If nothing is pending it's a no-op.
Step 5 — Clear and rebuild caches
php artisan optimize:clear # clears config, route, view, event, compiled caches
php artisan filament:clear-cached-components # REQUIRED when the release adds/renames Filament pages
php artisan view:cache # optional: precompile views (small speed win)
Filament cached components When Filament's component cache is enabled, newly shipped pages/resources are invisible until php artisan filament:clear-cached-components runs. "I updated but the new menu entry doesn't appear" is almost always this command.
Step 6 — Restart workers
php artisan queue:restart
Supervised workers (queue:work) keep old code in memory until restarted. The cron-based --stop-when-empty pattern picks up new code automatically on the next minute; the cron.php runner likewise.
Then bring the app back:
php artisan up
Step 7 — Verify
GET /api/v1/health→{"status":"ok",...}.- Load
/admin,/super-admin,/docs— no 500s, styles intact. - Send a test e-mail (queue → mail path).
- Submit the workspace's test lead (Meta/webhook path).
- Watch
storage/logs/laravel.logand the update screen's own log for 10 minutes. - Confirm
failed_jobsisn't suddenly growing.
Step 8 — Rollback (if something is wrong)
Roll back in this order, and stop as soon as the app is healthy:
-
Files: restore the previous release — your own copy of the app folder from before the update, the host's full-account backup, or your deployment system's previous revision.
-
Database — only if the new version ran destructive migrations and data is wrong: restore the Step 0 archive (Backups & restore). Note the archive predates a few minutes of live activity; you'll lose whatever happened after the backup.
-
Caches & workers after either rollback:
php artisan optimize:clear php artisan filament:clear-cached-components php artisan queue:restart -
Verify with the Step 7 checks.
migrate:rollback is a dev tool here Prefer restoring from backup over rolling migrations back on production. Some migrations drop columns/tables with data that a down() cannot rebuild. If you must, run php artisan migrate:rollback --step=1 --force immediately after confirming the previous code is in place, and re-verify the schema.
Common mistakes
- Updating without a backup (or with an old one).
- Skipping
filament:clear-cached-componentsafter a release with new pages — features stay invisible while everything "looks fine". - Forgetting
queue:restartwith supervised workers → half the system runs old code. - Letting a zip extraction overwrite
.envorstorage/. - Running
composer installwith the wrong PHP binary (system 8.1 vs host 8.4). - Updating straight to the newest version across many releases without reading notes — new env keys, new permissions, new migrations stack up.
Troubleshooting
| Symptom | Fix |
|---|---|
| New page/menu missing after update | php artisan filament:clear-cached-components |
| 500 after update | storage/logs/laravel.log; usually a stale compiled config — optimize:clear; or a missing new .env key |
| Styled-but-broken admin UI | Re-publish Filament assets: run the in-app updater's asset step (php artisan filament:assets) |
| Jobs still running old code | queue:restart (or wait one cron cycle on the cron-drain pattern) |
| Update screen can't reach the server | LEADHUB_UPDATE_SERVER reachable? outbound HTTPS allowed by the host? |
| Migrations fail halfway | Restore the backup to a consistent state; fix the cause (often MySQL version) before retrying |
Technical details
- In-app updater:
App\Services\UpdaterService(applies zip, clears caches, republishesfilament:assets, refreshes vendor/composer classmap metadata, opcache reset where available). - Cache healer:
App\Support\PostUpdateCacheHealer(compares shipped version vsstorage/framework/leadhub-version.stamp; purges stale compiled caches). - Version check:
App\Console\Commands\CheckForUpdates(leadhub:check-updates). - Filament command:
filament:clear-cached-components(Filament v4). - Release notes: the version also lives in
config/leadhub.php(LEADHUB_VERSION).
Related guides
Was this page helpful?
Thanks — noted. ✦