Your SQLite database is silently getting wiped on every Forge deploy

Running SQLite on a Laravel Forge site with zero-downtime deployments? Every deploy quietly throws your entire production database away. Here is why it happens and how to fix it by moving the database into the shared storage directory.

Zacharias Creutznacher Zacharias Creutznacher

Your SQLite database is silently getting wiped on every Forge deploy

SQLite is a perfectly reasonable choice for small-to-medium Laravel apps. It is fast, has zero operational overhead, and with WAL mode it handles a lot more concurrency than most people think. You can run an entire production site on a single file.

Until you deploy, and discover the file is gone.

#What actually happens

Laravel Forge's zero-downtime deployments work by cloning each new release into its own directory under releases/ and then flipping a current symlink to point at it. The old release stays on disk for rollback purposes, but it is no longer "live". Anything that lived inside the release directory (which is the default for database/database.sqlite) effectively disappears the moment the symlink flips. Every row you wrote is gone, and the next request creates a brand new empty SQLite file inside the fresh release.

That is exactly what zero-downtime deployments are supposed to do: treat each release as immutable and disposable. The catch is that an SQLite database is the opposite of disposable, so you have to tell Forge explicitly that this one file needs to survive the release cycle.

#The solution: put the database inside shared storage

Forge has a first-class concept for this called shared paths. A shared path is a file or directory that lives once, at the site root (for example /home/forge/example.com/), and gets symlinked into every new release. Forge configures both .env and the storage/ directory as shared paths by default on every zero-downtime site, which is why your environment variables and uploaded files survive deploys.

That default is exactly what we need: the cleanest place to put your SQLite database is inside storage/. Since storage/ is already shared, a file like storage/database/database.sqlite will automatically persist across every release without any extra Forge configuration. You can verify or tweak the shared path list under SettingsDeploymentsShared Paths, but in most cases you do not have to touch anything there.

Why not just share the whole database/ directory instead? Because that directory also contains your migrations/, seeders/, and factories/ folders, which are code that must ship fresh with each release. Sharing the entire directory would freeze those files to whatever version was on disk when you set up the shared path, and new migrations would never make it onto the server. Sharing storage/ side-steps the problem entirely: the database lives in a directory that is already meant to hold persistent state.

#Point the SQLite connection at the new file

With the file now living at storage/database/database.sqlite, the default SQLite connection in config/database.php is pointing at the wrong place. Update the database key of the sqlite connection to use storage_path() instead of Laravel's default database_path():

 1'sqlite' => [
 2    'driver' => 'sqlite',
 3    'url' => env('DB_URL'),
 4    'database' => env('DB_DATABASE', storage_path('database/database.sqlite')),
 5    'prefix' => '',
 6    'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
 7    // ...
 8],

If you set DB_DATABASE in your .env, use the absolute path to the shared file, for example /home/forge/example.com/storage/database/database.sqlite, so there is no ambiguity.

#Copying an existing database

If you already have data on the server that you want to keep, SSH in and copy the file into the shared storage directory before the next deploy:

 1mkdir -p /home/forge/example.com/storage/database
 2cp /home/forge/example.com/current/database/database.sqlite \
 3   /home/forge/example.com/storage/database/database.sqlite

#Trigger a deploy to make it stick

Nothing you just changed is active yet: the config update lives in Git, and the copied database file is just sitting in the shared directory. Push the config change and trigger a deployment (Forge's Deploy button, or a push to the deploy branch). Once the new release activates, Laravel will read from storage/database/database.sqlite via the symlinked shared storage/ directory, and every subsequent deploy will leave that file untouched.

From this point on your database is persistent. You can delete the stale file in the old release directory once you are sure the new setup is working.

#Takeaway

Zero-downtime deployments are great, but they assume your releases are stateless. Anything that has to survive a deploy (an SQLite database, user uploads, persistent logs) needs to live outside the release directory. For SQLite on Forge the cleanest setup is: share storage/, put the database inside storage/database/, and update config/database.php to match. One-time setup, no more data loss.