The database file is growing too large because SQLite keeps deleted data around, even though it’s no longer visible to your application.

Here’s what’s actually happening: When you DELETE or UPDATE rows in SQLite, the space they occupied isn’t immediately reclaimed and shrunk from the file. Instead, SQLite marks that space as free and available for reuse by future INSERT operations. This is a performance optimization to avoid expensive file system operations for every deletion. However, if you have a lot of churn (deletes and updates) without subsequent inserts filling that space, or if you delete large amounts of data, the file size can balloon unnecessarily, consuming disk space and potentially slowing down operations due to increased I/O.

Common Causes and Fixes:

  1. Unreclaimed Free Space After Deletions/Updates:

    • Diagnosis: Check the file size using ls -lh your_database.sqlite. Then, run PRAGMA freelist_count; within an SQLite client. If freelist_count is a significantly large number, it indicates a lot of unused space is being held.
    • Fix: Execute VACUUM; from an SQLite client connected to your database.
    • Why it works: VACUUM rebuilds the entire database file from scratch, copying only the live data and discarding all the marked-as-free space. It effectively defragments and shrinks the file.
  2. WAL Mode Not Enabled (or not fully effective):

    • Diagnosis: Check your WAL mode status: PRAGMA journal_mode;. If it’s DELETE or TRUNCATE, you’re not using Write-Ahead Logging.
    • Fix: Enable WAL mode by executing PRAGMA journal_mode=WAL;. You’ll likely need to reconnect to the database for this to take full effect. After enabling, you may still need to run VACUUM if there’s significant bloat already.
    • Why it works: In WAL mode, changes are written to a separate -wal file. The main database file (.sqlite) is only appended to, and free space management is handled more efficiently by checkpoints, which can reduce the need for manual VACUUM operations as frequently. However, VACUUM is still the definitive way to shrink the main file.
  3. Large Transactions Leaving Bloat:

    • Diagnosis: If you’ve performed very large DELETE statements or a series of massive UPDATEs within a single transaction (or across a few large ones), significant free space can be created. PRAGMA freelist_count; will show a high number.
    • Fix: Break down very large delete/update operations into smaller batches. After batch operations, run VACUUM;.
    • Why it works: Smaller transactions are less likely to create massive contiguous blocks of free space that the database then has to manage. VACUUM still cleans up any remaining bloat.
  4. Frequent Small Deletes/Updates:

    • Diagnosis: Even many small deletions or updates can fragment the database file over time, leading to the freelist growing. PRAGMA freelist_count; will be high.
    • Fix: Periodically run VACUUM; as part of a maintenance routine.
    • Why it works: VACUUM consolidates the free space, making it available for reuse and reducing the overall file size.
  5. Database Corruption (Rare but possible):

    • Diagnosis: If VACUUM fails with an error, or if you suspect corruption, run PRAGMA integrity_check;. A non-zero freelist_count on a corrupted database might be a symptom.
    • Fix: If integrity_check reports errors, the safest bet is to dump the database and restore it:
      sqlite3 your_database.sqlite .dump > dump.sql
      sqlite3 new_database.sqlite < dump.sql
      
      Then, VACUUM the new_database.sqlite if needed.
    • Why it works: Dumping and restoring effectively rebuilds the database from the logical data, discarding any underlying file-level corruption that might be preventing proper space management.
  6. Temporary Tables Not Cleaned Up:

    • Diagnosis: While less common for overall file size bloat, poorly managed temporary tables could contribute. Check for unusual file growth after complex queries.
    • Fix: Ensure your application logic properly closes connections or manages transaction scopes so that temporary tables are automatically dropped when no longer needed. Running VACUUM can also help consolidate space if temporary table remnants contribute.
    • Why it works: VACUUM cleans up all unused space, including space that might have been occupied by temporary tables that were supposed to be dropped but weren’t for some reason.

After successfully running VACUUM, you might encounter errors related to temporary tablespace if your database was severely fragmented and large.

Want structured learning?

Take the full Sqlite course →