Table of Contents
- Status page & health check
- Database size: the query to run
- Finding 09/10/2026: orphaned Laravel Telescope tables (≈6.6 GB)
- Removing Telescope safely
- Local storage inspector (
/storage-inspector) - Logs
Status page & health check
GET /on the backend host is the status page (StatusController): API, database, cache, queue, local storage +storage:link, R2, Turnstile and mail checks, with manual re-checks on/status/mail-check,/status/storage-checkand/status/r2-check(throttled per IP).GET /healthredirects toGET /api/health.GET /api/local_storage_statsandGET /api/r2_storage_statsgive the numbers (partition usage, files area size and growth, R2 free-tier usage) — see File Management & Storage API.
Database size: the query to run
Plesk → Databases → phpMyAdmin → SQL tab. Replace the schema name with the environment’s database (theteamdb_prod, theteamdb_test, theteamdb_dev):
TABLE_ROWS is an InnoDB estimate, not a count. Run it once a month (or after any incident) and
compare with the previous result: a normal month moves the real tables by a few MB.
Finding 09/10/2026: orphaned Laravel Telescope tables (≈6.6 GB)
Result of the query above ontheteamdb_prod:
Two Laravel Telescope tables hold ≈98 % of the
database; the whole application data is ≈0.1 GB.
Telescope is not in the backend repository. Checked on 09/10/2026 on
LMS_Backend main: no
laravel/telescope in composer.json or composer.lock, no config/telescope.php, no
TelescopeServiceProvider, no *_create_telescope_entries_table migration, nothing in
bootstrap/cache/packages.php, and git log --all -S telescope returns nothing on any branch. The
package was either installed directly on the server (outside git) or removed from the code without
dropping its tables. Nothing in the application reads those tables, so removing them cannot break the
app — once the server is confirmed to match the repository (step 1 below).telescope_entries (one row each, JSON content) and telescope_entries_tags. Without
telescope:prune on the scheduler and with TELESCOPE_ENABLED=true, a production API adds tens of
thousands of rows a day forever.
Removing Telescope safely
1
Confirm the server matches the repository
On the production server (SSH, or Plesk → Websites & Domains → SSH Terminal), inside the
backend root (the folder holding All empty / “not found” → Telescope is already gone from the code: skip to step 3.
Anything found → Telescope IS installed on the server: do step 2 first.
artisan):2
Only if Telescope is installed on the server: remove the package in this order
The order matters. If If the server runs from a git checkout, commit these changes to
App\Providers\TelescopeServiceProvider is still listed in config/app.php
when the package disappears, every php artisan call (including the package:discover that
composer runs after removing a package) dies with “class not found”.LMS_Backend so the next deploy
does not bring Telescope back.3
Prove nothing writes to the tables any more
phpMyAdmin → SQL. Run it twice, five minutes apart (and after a few API calls). The value must not
change:(
sequence is Telescope’s auto-increment key. Avoid COUNT(*) here: on 7.5 M rows it is slow
for nothing.)4
Check that InnoDB stores each table in its own file
ON (the MySQL default since 5.6) → DROP TABLE deletes the table’s .ibd file and the disk
space is back immediately. OFF → the rows live in the shared ibdata1; dropping still frees
the space for MySQL to reuse, but the file on disk never shrinks (only the hosting provider can
rebuild the instance). Either way the drop is safe.5
Drop the tables (child table first) and forget the migration
No data backup is needed: these rows are request / query debug logs, 6 GB of them, useless once
Telescope is gone. If you want the empty structure back one day, a schema-only dump is enough and
takes a second:
mysqldump --no-data theteamdb_prod telescope_entries telescope_entries_tags telescope_monitoring > telescope_schema.sql
(a full Plesk “Export Dump” would be 6+ GB — do not bother).DROP TABLE on a 6 GB table takes a few seconds (it unlinks a file); run it off-peak anyway.
If phpMyAdmin shows a timeout, the statement still finishes on the server: refresh and check
SHOW TABLES LIKE 'telescope%'; instead of running it again.6
Verify
Re-run the size query. Expected: the biggest table is now
quiz_questions (≈33 MB) and the whole
schema is ≈0.1 GB. Then open the status page and a few API endpoints — nothing in the app touches
the dropped tables, so nothing changes.Do not use
php artisan telescope:prune or telescope:clear to empty the tables: they delete
1,000 rows per iteration and would run for hours on 7.5 M rows — and they only exist while the
package is installed. DROP TABLE (or TRUNCATE, below) does the job in seconds.Alternative: keep Telescope, but bounded
Only if Telescope is still installed on the server and you want to keep it for debugging:TRUNCATE recreates the table, so the space comes back like a
drop; the foreign key check must be off for the parent table):
Local storage inspector (/storage-inspector)
Added 09/10/2026 (LMS_Backend: StorageInspectorController, app/Services/StorageInspectorService.php,
resources/views/storage_inspector.blade.php, app/Console/Commands/StorageInspectorUrl.php,
tests/Feature/StorageInspectorTest.php). A read-only, paginated HTML page answering “what is in
the local storage and who uses it?”. It never deletes or moves anything.
What it lists
Every file understorage/app (the local disk): the uploads in public/files/…, the bulk-upload
folders public/files/<folder>/…, and the certificate share images in certificate-images/…. For
each file:
References are resolved from:
- Foreign keys to
files.id:trainings.image_id/video_id/badge_logo_id,quizzes.image_id,quiz_questions.image_id,users.image_id,companies.company_image_id,certification_configs.company_logo_id/signature_id/cachet_id,landing_pages.header_image_id/header_background_image_id/banner_image_id,landing_page_partner_companies.company_logo_id,landing_page_featured_categories.feat_category_image_id,user_background_items.logo_id,platform_theme_settings.platform_logo_id. - URL columns:
platform_theme_settings.platform_logo/auth_page_image,certification_configs.default_badge_logo,landing_page_partner_companies.company_logo. - Rich text that embeds
/storage/...links:lessons.content(reported with the lesson’s training),classrooms.content/classroom_description,trainings.description,quizzes.description,quiz_questions.label,landing_pages.company_description,users.public_profile_description. - Naming conventions:
certificate-images/<certification_id>-<fingerprint>.png(CertificateImageServicecache, regenerated on demand) andpublic/files/2fa-<user_id>-<time>.png(the 2FA enrolment QR codes).
Trust the page in one direction only: a file it marks
used is used. A file it marks orphan could
still be referenced by something the index does not know (a hard-coded URL in the frontend, a lesson
whose link does not contain /storage/), so look before deleting.
Access
The page answers 404 unless one of these is true:
The route is throttled (30 requests / minute / IP) and carries
noindex. Paging and filters keep
the token or signature on every link.
Filters & options
q (path, URL, model name, slug, label or #id), status, area (folder), sort
(modified · size · name · status · area), dir, per_page (10–200, default 50), page.
The disk scan and the database index are cached for STORAGE_INSPECTOR_CACHE_SECONDS (default 60);
refresh=1 — the rescan now link — rebuilds them.
Checks
Logs
- Laravel writes to
storage/logs/laravel.log(LOG_CHANNEL=stack→ single file by default; thedailychannel keeps 14 days). On Plesk: Files →httpdocs/storage/logs/, or Logs for the web server’s access / error logs. LOG_LEVEL=debugis the.env.exampledefault; useerrororwarningin production so the file stays readable.- The application also keeps its own tables:
history(audit log, see History & Activity Log API),email_logs,authentication_log,konnect_history. They grow slowly; the size query above tells when one of them needs pruning.