Handling SQLite with Capistrano

SQLite is very useful for rapid prototyping. It’s commonly used locally but it can also be used on remote servers. If you’re using Capistrano to manage server deployments then you’ll need to handle SQLite’s database file.

How Capistrano releases work

Capistrano works by creating a new directory for each release and creating a symlink for the latest (current) release. Here’s how the directory structure looks:

1
2
3
4
5
6
7
8
9
10
11
12
├── current -> /var/www/my_app_name/releases/20150120114500/
├── releases
│   ├── 20150080072500
│   ├── 20150090083000
│   ├── 20150100093500
│   ├── 20150110104000
│   └── 20150120114500
├── repo
│   └── <VCS related data>
├── revisions.log
└── shared
    └── <linked_files and linked_dirs>

SQLite’s database file is typically stored inside the Rails db directory. Because each deployment is created in a new directory, the database file gets ‘lost’ on each deploy.

Copying the DB

The obvious way to handle this problem is to copy the database file across, each time a new release is created. Here’s how the code for that looks:

1
2
3
4
5
6
7
8
9
namespace :deploy do
  task :copy_sqlite do
    on roles(:all) do |_host|
      execute "cp #{current_path}/db/production.sqlite3 #{release_path}/db/"
    end
  end
end

after 'deploy:updated', 'deploy:copy_sqlite'

Using the shared directory

However, there are some drawbacks to this approach. Firstly, it duplicates the database each time, spreading data and taking up server space. Secondly, it breaks Capistrano’s rollback feature because rolling back would revert to an older version of the database.

Luckily, there is a better way to fix the problem, using linked_dirs. Here’s how the code for that looks:

1
set :linked_dirs, %w[db/production.sqlite3]

This will move production.sqlite3 into the shared directory, which persists between releases. The current release will have a symlink that points to it. With this code you don’t need to worry about copying the database, the current release will always have access to production.sqlite3.