
How to Audit and Clean Up Unused Media Files in Drupal Using Entity Usage and Views
If you've been running a Drupal site for more than a year, you probably have a media library problem. Images from old blog posts, PDFs from outdated products, and videos from previous campaigns sit there taking up space. They slow down your backups, clutter your media browser, and make life harder for your content editors.
Our experience shows that most Drupal sites accumulate anywhere from 30% to 50% of completely unused media files within their first three years. One client recently discovered they were backing up 7,000 unused PDFs every night – files that hadn't been referenced anywhere on their site for over two years.
The good news? Drupal provides several effective ways to identify and remove these unused files. This guide walks you through the exact process using the Entity Usage module combined with Views, plus some alternative approaches if you need something simpler or more automated.
Prerequisites
Before you start cleaning up unused media files in Drupal, you'll need:
- Drupal 10 or 11 (these instructions work for both versions)
- Composer access to install modules
- Administrative permissions to configure modules and create Views
- A recent backup of your database and files directory
- SSH access to run Drush commands (recommended but not required)
You should also understand basic Views configuration in Drupal. If you've never created a View before, spend 15 minutes with the Views UI first to get familiar with the interface.
Step-by-Step Implementation for Media Cleanup
Step 1: Install Required Modules
Start by installing the Entity Usage module and Views Bulk Operations. These two modules form the foundation of your cleanup process.
composer require drupal/entity_usage drupal/views_bulk_operations drush en entity_usage views views_ui views_bulk_operations -y
If you want additional cleanup tools, also install:
composer require drupal/unused_media_cleaner drupal/orphans_media drush en unused_media_cleaner orphans_media -y
Clear your cache after enabling these modules:
drush cr
Step 2: Configure Entity Usage Tracking
Navigate to Configuration > System > Entity Usage or visit /admin/config/system/entity-usage directly.
On this configuration page:
- Check the box next to Media under "Track usage for these entity types"
- Enable tracking for any other entity types you want to monitor
- Save the configuration
Now comes the important part: you need to rebuild the usage tracking database. This process scans your entire site and creates a record of where each media item is currently being used.
Click the "Batch update" tab and then "Recalculate usage". Depending on your site size, this might take anywhere from a few seconds to several minutes.
Step 3: Create Your Unused Media View
This is where things get interesting. You'll build a custom View that shows only unused media items.
Navigate to Structure > Views and click "Add view".
Configure your initial View settings:
- View name: "Unused Media Audit"
- Show: Media
- Type: All
- Create a page: Yes
- Page path: /admin/content/unused-media
- Display format: Table
- Items per page: 50
Click "Save and edit" to enter the Views configuration interface.
Step 4: Add Essential Fields
In the Fields section, add these fields to give you useful information about each media item:
- Media: Name - Shows the media title
- Media: Type - Displays whether it's an image, document, video, etc.
- Media: Created - When the file was uploaded
- Media: Thumbnail - Visual preview for images
- File: URI - The actual file path
For each field, leave the default settings unless you want to customize labels or formatting.
Step 5: Configure the Entity Usage Relationship
This is the critical step that makes everything work.
Under the Advanced section on the right, find Relationships and click Add.
Search for "Entity Usage" and select "Entity Usage: Usage". In the configuration:
- Leave "Require this relationship" unchecked
- Apply the relationship
This relationship connects your media items to their usage tracking data.
Step 6: Enable Aggregation and Add Usage Count
Still in the Advanced section, find "Use aggregation" and set it to Yes.
Now go back to Fields and add a new field:
- Search for "Usage count" (it should appear under the Entity Usage relationship)
- Configure it to use SUM as the aggregation type
- Label it "Times Used" or similar
Step 7: Filter for Unused Media Only
Add a filter to show only unused media. Under Filter Criteria, click Add and search for "Usage count".
Configure the filter:
- Aggregation type: SUM
- Operator: Is empty (NULL)
This filter ensures you only see media items with zero usage across your site.
Step 8: Add Bulk Operations
To actually delete unused media, add Views Bulk Operations functionality.
Add a new field at the beginning of your field list:
- Search for "Views bulk operations"
- Select "Bulk update media"
- In the configuration, enable only the "Delete media" action
- Position this field first in your field list
Save your View. You now have a working unused media audit tool for Drupal sites.
Code Examples for Automated Cleanup
Sometimes you need to automate the cleanup process. Here's a custom Drush command that identifies and removes unused media:
getStorage('media');
// Load all media entities
$media_ids = $media_storage->getQuery()
->accessCheck(FALSE)
->execute();
$deleted_count = 0;
$batch_size = 50;
foreach (array_chunk($media_ids, $batch_size) as $chunk) {
$media_entities = $media_storage->loadMultiple($chunk);
foreach ($media_entities as $media) {
// Check if media is being used
$usage = $entity_usage->listSources($media);
if (empty($usage)) {
// Double-check it's not referenced in old revisions
if ($this->checkRevisions($media)) {
continue;
}
$this->logger()->notice('Deleting unused media: ' . $media->label());
$media->delete();
$deleted_count ;
}
}
}
$this->logger()->success("Deleted $deleted_count unused media items.");
}
/**
* Check if media is used in old revisions.
*/
private function checkRevisions($media) {
// Add your revision checking logic here
return FALSE;
}
}For a simpler approach, you can create a batch processing script:
/**
* Batch process to delete unused files.
*/
function custom_cleanup_batch_delete() {
$batch = [
'title' => t('Deleting unused media'),
'operations' => [],
'finished' => 'custom_cleanup_batch_finished',
];
// Get unused media IDs from Entity Usage
$query = \Drupal::database()->select('media', 'm')
->fields('m', ['mid'])
->condition('mid', \Drupal::database()->select('entity_usage', 'eu')
->fields('eu', ['target_id'])
->where('eu.target_type = :type', [':type' => 'media']), 'NOT IN');
$results = $query->execute()->fetchAll();
foreach ($results as $row) {
$batch['operations'][] = ['custom_cleanup_delete_media', [$row->mid]];
}
batch_set($batch);
}
/**
* Batch operation callback.
*/
function custom_cleanup_delete_media($mid, &$context) {
$media = Media::load($mid);
if ($media) {
$context['message'] = t('Deleting @label', ['@label' => $media->label()]);
$media->delete();
}
}Common Mistakes to Avoid
Deleting Without Verification
The biggest mistake people make is trusting the unused media report blindly. Some media might appear unused but actually be:
- Referenced in custom blocks that Entity Usage doesn't track
- Embedded using absolute URLs in body fields
- Used in email templates or external systems
- Referenced in Views header/footer text areas
Always spot-check a sample of files before bulk deletion. Pick 10-20 files marked as unused and manually verify they're truly not needed.
Ignoring Old Revisions
We've found that Entity Usage sometimes misses references in old node revisions. If you have content moderation enabled or keep extensive revision histories, media that appears unused might still be referenced in previous versions of content.
Before deleting, consider whether you need those old revisions. If not, clean them up first using the Node Revision Delete module.
Not Checking File System References
Some sites reference files directly through their file paths rather than media entities. Run this query to check for direct file references in text fields:
SELECT COUNT(*) FROM node__body WHERE body_value LIKE '%/sites/default/files/%';
If you find many direct references, you'll need a different cleanup approach that scans text fields for file paths.
Forgetting About Translations
On multilingual sites, media might be used in translated versions of content that Entity Usage doesn't detect properly. Always test your cleanup process on translated content before running it site-wide.
Running During Peak Hours
Batch deletion operations can slow down your site. Schedule cleanup during low-traffic periods or use queue processing to spread the load over time.
Testing and Verification Steps
Before you delete anything permanently, follow this verification process:
1. Export Your Unused Media List
From your unused media View, export the results to CSV. This gives you a record of what you're planning to delete and allows for manual review.
2. Test with a Small Batch
Select 5-10 unused media items and delete them manually. Then:
- Clear all caches
- Check the pages where these media items were previously uploaded
- Verify no broken images or missing file errors appear
- Check your logs for any related warnings
3. Verify Entity Usage Accuracy
Teams we work with report that Entity Usage is about 95% accurate out of the box. To verify its accuracy on your site:
- Pick a media item that shows as "used"
- Click through to see where it's supposedly being used
- Visit those pages and confirm the media actually appears
- Repeat for several media items
4. Check Your Backups
Ensure your backup system is working before any bulk deletion:
# Create a manual backup drush sql-dump --gzip > backup-before-cleanup.sql.gz # Verify the backup file size looks reasonable ls -lh backup-before-cleanup.sql.gz
5. Monitor After Deletion
After removing unused media, monitor your site for 24-48 hours:
- Check your error logs for missing file warnings
- Ask content editors to report any missing images
- Review your 404 reports for attempts to access deleted files
6. Performance Verification
After cleanup, you should see improvements in:
- Media library loading time (measure before and after)
- Backup duration and size
- Database query performance for media-related operations
Document these improvements to justify regular cleanup schedules.
Conclusion
Cleaning up unused media files in Drupal doesn't have to be intimidating. With the Entity Usage module and a properly configured View, you can safely identify and remove unnecessary files that are slowing down your site. The key is to approach it methodically: install the right tools, configure them carefully, verify your results, and always keep backups.
Remember that media cleanup isn't a one-time task. Set up a quarterly review process to keep your media library under control. Your content editors will thank you for the cleaner interface, and your systems administrator will appreciate the smaller backups and faster site operations.
If you're managing a large Drupal site with thousands of media files and need help setting up an automated cleanup process that fits your specific workflow, we can help you build a custom approach that safely identifies unused media while preserving everything your content team actually needs. Our team has experience with complex media migrations and cleanup operations that require careful handling of historical content and multilingual considerations.
