SQLite cannot write to your database because the directory containing it lacks write permissions. Both the database file and its parent directory must be writable for SQLite to function properly.
SQLite needs to create temporary and journal files (such as -journal, -wal, and -shm files) in the same directory as your database file during normal operations. If the directory containing the database lacks write permissions, SQLite cannot create these essential files and will fail with the SQLITE_READONLY_DIRECTORY error. This happens even if the database file itself is writable—directory permissions take precedence. SQLite uses these temporary files for transaction management, write-ahead logging, and crash recovery. Without write access to the directory, the database becomes effectively read-only to protect data integrity.
First, verify what permissions the directory currently has and who owns it:
ls -ld /path/to/database/directoryLook for output like:
drwxr-xr-x 5 root root 4096 Dec 22 10:15 /path/to/databaseThe r(read), w(write), x(execute) characters show permissions. If the owner or group cannot write (w), that's your problem.
Determine which user your application or web server is running under:
# For web servers
ps aux | grep -E 'apache|nginx|www-data|php-fpm'
# For Node.js apps
ps aux | grep node
# For Python apps
ps aux | grep pythonNote the user in the first column (e.g., www-data, nobody, myappuser). This is the user that needs write access.
Once you know the user and group that should own the database directory, update ownership and permissions:
# Change owner to your application user (e.g., www-data for web servers)
sudo chown www-data:www-data /path/to/database/directory
# Grant read, write, and execute permissions to owner
sudo chmod u+rwx /path/to/database/directory
# Also grant write permissions to group if your app runs as a group
sudo chmod g+w /path/to/database/directoryFor most web applications, the safest approach is:
sudo chown -R www-data:www-data /path/to/database/directory
sudo chmod 755 /path/to/database/directory
sudo chmod 644 /path/to/database/directory/*.dbThe database file also needs to be readable and writable:
# Make sure the database file is readable and writable
sudo chmod 644 /path/to/database/directory/database.db
# Or if you want stricter permissions (owner only)
sudo chmod 600 /path/to/database/directory/database.dbIf the file is owned by root but your app runs as www-data, change ownership:
sudo chown www-data:www-data /path/to/database/directory/database.dbIf SQLite crashed or was interrupted, there might be leftover journal files:
# List all journal and temporary files
ls -la /path/to/database/directory/ | grep -E '\.db-journal|\.db-wal|\.db-shm'If you find them and the database is not currently in use, you can safely delete them:
rm /path/to/database/directory/*.db-journal
rm /path/to/database/directory/*.db-wal
rm /path/to/database/directory/*.db-shmDo NOT delete these files while the database is being accessed by a running application.
Test that writes now work. Using the sqlite3 CLI:
sqlite3 /path/to/database/directory/database.dbThen in the SQLite prompt:
CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO test (name) VALUES ('test_entry');
SELECT * FROM test;
.quitIf these commands succeed, the permissions are now correct. Drop the test table if you created it:
sqlite3 /path/to/database/directory/database.db "DROP TABLE test;"Web Server Scenarios: If your SQLite database is served by a web application (PHP, Node.js, Python, etc.), the web server user (typically www-data on Ubuntu/Debian, apache on CentOS, or _www on macOS) must have write access. Never run web servers as root for security reasons.
Docker & Containers: In Docker, ensure your Dockerfile sets proper permissions on the database directory. Use CHOWN in your Dockerfile or ensure the container runs with a user that has the appropriate permissions. Example:
RUN chown -R app:app /app/data
RUN chmod 755 /app/dataWAL Mode: If using Write-Ahead Logging (WAL) mode, SQLite creates additional .db-wal and .db-shm files alongside the database. These also need to be in a writable directory.
SELinux & AppArmor: On systems with mandatory access controls (SELinux or AppArmor), even correct file permissions may not be enough. Check your security policies: getenforce for SELinux or aa-status for AppArmor. You may need to adjust policies or labels to allow writes.
Read-Only Deployments: Some production scenarios intentionally keep databases read-only (e.g., replicas, archival). If this is intentional, open the database with the URI parameter ?mode=ro to avoid errors: sqlite3:///path/to/db?mode=ro
SQLITE_CORRUPT_VTAB: Content in virtual table is corrupt
Content in virtual table is corrupt
SQLITE_IOERR_WRITE: Disk I/O error during write
Disk I/O error during write operation
SQLITE_READONLY: Attempt to write a readonly database
How to fix "SQLITE_READONLY: Attempt to write a readonly database" in SQLite
SQLITE_CONSTRAINT_PRIMARYKEY: PRIMARY KEY constraint failed
How to fix "SQLITE_CONSTRAINT_PRIMARYKEY" in SQLite
SQLITE_READONLY_DBMOVED: Database file has been moved since opened
How to fix 'SQLITE_READONLY_DBMOVED: Database file has been moved since opened'