TL;DR — I reduced deploy times from 5 minutes to less than 15 seconds by replacing the standard Capistrano deploy tasks with a simpler, Git-based workflow and avoiding slow, unnecessary work.1
I worked on this deploy recipes while working for @mickael from ProcessOne. If you enjoy challenges and look for a Ruby job, you should talk to us.
I’ve recently went from 5 minutes to about 15 seconds for my deploys. That feels much better… Spending minutes (or longer) for deploying a regular Rails application was pissing me off, I want to deploy within seconds. This great article from Code Climate convinced me I should just change the way capistrano works, and do it my own way.
What I thought would take me a few hours work ended up taking me days of full time work, and a lot of hassles. I first looked at recap, but it’s based on capistrano2, looked at mina but it’s single host based. Fabric seems nice but I didn’t feel like it either.
I ended up using capistrano3 and tweaking it a lot. What capistrano now really is is a bunch of gems combined, the most important being sshkit.
The benefit of keeping capistrano3 while doing your own way is you get all the
capistrano3 plugins. The downside is you must be extra careful when adding any.
One example amongs many is
capistrano-rails, it runs
db:migrate on your server for every single deploy, even you don’t need to run
it. That task alone takes at least 4 seconds (loading the rails stack), even
you don’t need it for most of your deploys. It also run
deploy:assets:precompile every time, boom goes for another 4 seconds. You’ve
just lost 8 seconds looking at your console for no reason.
Another issue with
capistrano3 is the way it manages
git, it doesn’t keep
.git subdirectory, it caches a local clone and copies files on deploy.
That’s slower, but it also means I can’t edit files on server to try things
out quickly and use
git diff to view my changes.2
What I want for my deploys:
- git based
- done within seconds, not minutes
- being able to rollback
- locks, should prevent multiple deploys at once
What I found was slowing the deploys with capistrano2:
- Everytime you run a remote rails task, it takes 4 to 5 seconds to just load
the Rails stack.
rake db:migrateeven you don’t need to? 5 seconds extras,
rake assets:precompile? here goes another 5 seconds, etc.
runyou use does a single SSH connection, and capistrano 2 doesn’t have a
connection_pool. It’s easy to forget about that, add
runcommands, and end up with a minute spent connecting new ssh connections. I suggest upgrading to
sshkitjust for that connection_pool feature. I was looping to do extra
lnfor multiple files and each one would take an extra second.
- On my setup, compiling assets on the server side is painfully slow.
Compiling on my laptop and using
rsyncto copy the compiled files remotely vastly improved speed.
- Remote assets compilation was broken, it wasn’t using the cache directory and would be rerun for every deploys.
- Be extra careful about your assets, try to make them small instead of just
@import. I suggest you use public CDN for known projects like jQuery, Bootstrap, etc. It will be faster for your website too.
- Using an old
sidekiqway to restart was slow, it would also load the Rails stack, I’ve moved that to using an instant
kill -USR1and you can benefit from it as my patch is now included in capistrano-sidekiq since 0.2.7. Just use
set :sidekiq_use_signals, truein your deploy file.
What my new deploys do:
- Create a lock file on the server for not running multiple deploys at once
- Quiet sidekiq
- Fetch the git repo, and checkout the proper commit
- Check if bundle install is required, if yes run it
- Check if asset compilation is required, if yes run it
- Check if migration is required, if yes run it
- Check if whenever crontab should be changed, if yes run it
- Tag the local git repo to view past deploys, but doesn’t push those tags back to the git server
- Restart rainbows/unicorn/puma
- Stop sidekiq (gets started by
- Remove the lock
My new deploys now take 20 seconds or less:
I have not published any code but might if enough people is interested. I’ve
just wrote my set of capistrano tasks and replaced the default
cap deploy using
I hope you found this post useful. If you did, drop me a note at @fabienpenso. If you didn’t, drop me a note anyway telling me how to improve it.
You can stay up to date via my RSS feed.
This quote is stolen from this great article from Code Climate. It’s also the article who convinced me to do the same way they did, except they used capistrano2 and I used capistrano3 ↩
I know, I know. You shouldn’t do that but sometimes you need to anyway, or you’re just modifying files on your staging servers. ↩