EgorkZe
EgorkZe

Reputation: 403

How to deploy Rails sqlite3 database with Capistrano?

I deploy like this:

bundle exec cap deploy:cold
RAILS_ENV=production rake db:migrate
bundle exec cap deploy:migrate

Error in log file:

I, [2014-04-14T14:15:14.853543 #10769]  INFO -- : Started GET "/users/sign_up" for
176.192.228.14 at 2014-04-14 14:15:14 -0400
I, [2014-04-14T14:15:14.856055 #10769]  INFO -- : Processing by
Devise::RegistrationsController#new as HTML
I, [2014-04-14T14:15:14.857398 #10769]  INFO -- : Completed 500 Internal Server Error
in 1ms
F, [2014-04-14T14:15:14.860844 #10769] FATAL -- :
ActiveRecord::StatementInvalid (Could not find table 'users')

But in the current/db folder was created production.sqlite3. On localhost:3000 it works. How can I migrate the database for production with Capistrano?

I use Nginx and Unicorn and this is my repository.

Upvotes: 2

Views: 1663

Answers (3)

Matthew
Matthew

Reputation: 1615

There are two good options for this; Copying or Linking. Here are some reasons to choose one or the other, followed by how to do it with modern, or not so modern, Rails.

Reasons For

Copying:

  • Leans into the simplicity of deploying using SQLite (it's just a file)
  • Gives you a point in time backup of the database (on the same machine/volume, but better than nothing)
  • Makes rolling back a deploy easier in an emergency (no need to carefully roll back schema changes, although you will also lose any data changes since the deploy)

Linking:

  • Saves space (may be the only option if your database is large or disk is small)
  • Deploys may be quicker, depending on the size of your DB and how your filesystem performs a copy

My guess is that most people who choose SQLite do so for ease of administration in which case copying the database is probably the right option, if you can afford the space. Assuming you have Capistrano set to only keep 5 releases and your database isn't huge, this should be very doable.

Copying

If you built your deploy workflow from scratch, you can simply add a new copy_db task and tell it to run before you run db:migrate:

task :copy_db, roles: :app do
  run "cp #{current_path}/storage/production.sqlite3 #{release_path}/storage/"
  # For Rails < 7.1:
  # run "cp #{current_path}/db/production.sqlite3 #{release_path}/db/"
end

If you're using the capistrano-rails gem, you can create the task and have it run before every deploy:migrate using a before hook:

# lib/capistrano/tasks/copy_db.rake
namespace :deploy do
  desc "Copy the database from the current deploy to the new release"
  task :copy_db, roles(:app) do
    run "cp #{current_path}/storage/production.sqlite3 #{release_path}/storage/"
  end
  before 'deploy:migrate', 'deploy:copy_db'  
end

Linking

Use append :linked_dirs to link the storage/ folder in your deploy to the shared folder. To do this add append :linked_dirs, "storage" to your config/deploy.rb or config/deploy/<stage_name>.rb file.

You may already have this command to link your log/ or .bundle/ folders in which case you can just add "storage" to the end of the list: Screenshot of diff on GitHub showing the addition of "storage" to the list of linked directories

If you're on Rails < 7.1 the database lives in the db/ directory and you probably don't want to link the whole directory. In that case you can just link the database file by adding append :linked_files, "db/production.sqlite3" to your config/deploy.rb or config/deploy/<stage_name>.rb file.

Remember, just like if you're using a shared Postgres/MySQL database, if you rollback your deploy, you may need to rollback your database migrations too. One way to handle this could be to add a rollback_db task and set it to automatically run after rollback with:

after 'deploy:reverted', 'deploy:rollback_db'

Linking and Copying

If you want the best (worst?) of both worlds you could link the storage/ folder and make a copy of the database in that folder (named for the release) every time you deploy. It would also be up to you to delete excess copies of the database, probably after deploy:cleanup. Checkout the deploy:cleanup task for a pattern of how to go about that.

Upvotes: 0

Mark Murphy
Mark Murphy

Reputation: 2874

Better yet, change up your db configuration:

production:
  adapter: sqlite3
  database: /absolute/path/to/shared/db/production.sqlite3 # instead of db/production.sqlite3

Upvotes: 7

matanco
matanco

Reputation: 2129

Working with Sqlite in production is very problematic because each time you deploy new version you entiredb is stay on the old release folder, what you can do is when you deploy add this command:

task :copy_sqlite, roles: :app do
    run "cp #{current_path}/db/production.sqlite3 #{release_path}/db/"
end

just add this the before rake db:migrate and it will solve your problem.

My strong suggestion move to PostgreSQL/MySQL.

Upvotes: 3

Related Questions