Rails deployment made easy with Inploy

After working in some systems made in Rails, Capistrano became one of the things that began to bother me more and more. I tried to find other alternatives, including Vlad, but none satisfied me, so I decided to create a solution and called it Inploy.

Before Capistrano fans crucify me, I wanted to let it clear that I don’t think Capistrano is a bad tool, just that it’s bad for my needs, which are much simpler than Capistrano can attend.

One of the things I don’t like about Capistrano is that it creates a lot of folders, one for each release. Some people argued with me that they are useful when you want to rollback a release, but that never happened to me, as never happened with most of the people I talked with. The rest of the people told me that in some circumstance they had to rollback a deploy, but the circumstance was that the deployed code broke the build.

Inploy uses Git, so in case we need to rollback our deploy to a version, we can use git reset. Of course this solution is more limited that the one that Capistrano uses, but it’s a solution to a problem that probably should never occur, a problem caused probably by bad practices in the development.

As a consequence of Capistrano strategies, they deploys are a little slow for my taste. I know there are some configurations that can speed up the process, but to configure is a thing I don’t want to do. I want to use a tool that works the best way by default and in most cases, the best way to a fast deploy is the simplest one: git clone in the setup and git pull in the update, the same that Inploy does.

Speaking about defaults, it’s very annoying to me having to define in Capistrano how to restart the server, how to clean the cache and what tasks it should rake, like more:parse and asset:packager:build_all. Inploy tries to solve this problems by executing common tasks by default, being smart enough to identify what tasks it should rake and in which order. The idea behind the plugin is that you shouldn’t worry about how to deploy that new tool you’re using, that things just work without the possibility of something going wrong.

Another advantage from Inploy over Capistrano for most cases is that it just have one way to deploy and its called update. This task updates the code, migrates the database, does some other tasks and restarts the server. It makes not sense to me to update the code without running migrations. In all teams I worked with, everyone could deploy the application and in most cases someone executed cap deploy instead of cap deploy:migrations. I know this is a human error but I prefer to use a tool that doesn’t let people make mistakes.

A need I had with deployment tools is that it could work both remotely and locally. I worked in a project that was delivered to a foreign client and the people responsible of updating the application in each release couldn’t do it otherwise that by connecting to a server and running a script. All of them used Windows and were used to work with Java systems that required them to access a machine, replace a war file and execute some commands. In that opportunity, the solution was to create a deploy.sh file in the root folder on the application. This file was executed by somebody from the client every time they needed to update the application.

The problem mentioned above doesn’t happen with Inploy, as it can be run both remotely or locally. When running remotely, the only only thing it does is to connect to a list of servers and run the tasks like being locally logged. It’s because of this that Inploy is delivered as a plugin, so it’s available in all environments.

As today, Inploy will only work for you if you’re working with Git and Passenger.

To install Inploy you can execute:

script/plugin install git://github.com/dcrec1/inploy.git

On installation, Inploy will write to a file in config/deploy.rb. This file is read by the plugin and it should be something like this:

deploy.application = "inploy"
deploy.repository = 'git://github.com/dcrec1/inploy.git'
deploy.user = 'dcrec1'
deploy.hosts = ['hooters', 'geni']
deploy.path = '/var/local/apps'

After that, we’re ready to execute the tasks that the plugin has:

rake inploy:local:setup
rake inploy:local:update
rake inploy:remote:setup
rake inploy:remote:update

I created a short video in which I demonstrate how to use the different tasks of the plugin. In the video I’m inside the Signal project, which already has Inploy installed and I remove it along with the configuration file to demonstrate how easily it is to install and configure it. After that I execute a remote setup and then a remote update. We can see in the video that the commands that Inploy runs are logged with the corresponding outputs and that config/*.sample files are renamed to config/*, another feature of Inploy. In the second part of the video a local update is ran and then deleted the repository, cloned and executed another setup, this time locally.

Deploy easily to Locaweb with Inploy from Diego Carrion on Vimeo.

More details about the plugin can be obtained in the official Inploy repository, where I’ll try to keep the README up-to-date. I hope it’s clear the way that Inploy works, but just in case, looking at the code is an option to eliminate doubts. The code is quite small (50 LoC moreless) and also self explanatory. Follows an snippet:

def local_setup
  copy_sample_files
  create_folders 'tmp/pids', 'db'
  run "./init.sh" if File.exists?("init.sh")
  after_update_code
end
 
def remote_update
  remote_run "cd #{application_path} && rake inploy:local:update"
end
 
def local_update
  run "git pull origin master"
  after_update_code
end
 
def after_update_code
  install_gems
  migrate_database
  run "rm -R -f public/cache"
  rake_if_included "more:parse"
  rake_if_included "asset:packager:build_all"
  run "touch tmp/restart.txt"
end

I wish this simple code motivates people to contribute with the project and make it smarter.

If this plugins helps you, please consider to recommend me at Working With Rails.

49 thoughts on “Rails deployment made easy with Inploy

  1. @bob

    I’ll make it a gem as soon as possible, maybe today. It didn’t happen yet because I didn’t know if somebody wanted this, but thanks for the feedback :)

  2. First of all, I love the tag line of your blog… we share common interests.

    Secondly, fine work. Just used this to deploy an app. Simple. Fast. Loverly.

    Thanks for sharing.

    • python 上有很多這種超快上手的小型web framework ( ex. web.py, boltte, flask), 只有在debug mode打開的情況下才會這樣吐. 到production 階段卻不想關debug mode 的想法我可以體會, 當測試嚴重不足的時候, 就會想叫user 幫忙debug了, 尤其是那種無法repeat的bug, 叫user 回報當時的traceback訊息最快.

  3. @brandon

    I worked in some projects where everybody shared the same database.yml, so it was normally versioned as other files. In this cases, the development environment uses sqlite as a database, so the user and password was not a problem.

    In some other projects where the development database needed to be MySQL, like when working with Sphinx, the database.yml was ignored by Git an instead a database.yml.sample was versioned. The reason for this is that each people had a diferente combination of user/password for the MySQL database, so the database.yml could not be shared.

    Inploy copies config/database.yml.sample to config/database.yml if the second file do not exists, so if you decided to use a config/database.yml.sample file, I recommend you to configure it for the staging and production environment. This way, when Inploy copies the file, the configuration will be ok and migration will run without problems.

    Thanks everybody for the feedback.

  4. Interesting. So, I guess that means if I’m using MySQL, the first time I deploy the app I need to manually upload the database.yml file? Because if it’s a public repository on GitHub, I obviously can’t track the db config file. So after I rake inploy:remote:setup, upload the database.yml file, I can just rake inploy:remote:update and everything should be cool?

  5. Yes, exactly that. As on setup the migrations will be executed, maybe it’s a good idea to configure database.yml.sample to use sqlite by default, so the process will not thrown an error and later will be easy to delete this database.

  6. Ah, I see. Yes, that makes sense. Thanks! I really wasn’t looking forward to having to learn to use Capistrano for something that really shouldn’t be all that complicated, but looks like now I won’t have to :)

  7. I would like to deploy my staging server first. If everything looks ok I want to proceed and deploy to the production server. Is that possible with Inploy?

  8. @Erik

    If you’re using the production environment in your staging server this is actually possible, If not, there is somebody working on making the environment configurable, I think this will available tomorrow.

  9. I can use the production environment on the staging server, no problem. But how do I specify which server to deploy to when running inploy:local:update ? I don’t want to deploy to production server until the application is tested ok on staging server…

  10. Is there a mailing list/forum for Inploy?

    For me the gem version isn’t working.

    rake inploy:remote:setup
    Don’t know how to build task ‘inploy:remote:setup’.

    I’m on REE 1.8.7 and Rails 2.3.4 on Snow Leopard.

    The plugin version works but when running inploy:remote:setup it stops after

    checkout -f -b master origin/master
    fatal: git checkout: branch master already exists

    So submodules are never initiated and updated and inploy:local:setup never runs.

  11. Thanks for the tip about the environments. Exactly what I was looking for. About the gem problem I’ll create a topic on the google group.

  12. Hi, very good plugin but I have a problem when it works with ports

    my deploy.rb:

    deploy.template = :locaweb
    deploy.application = “demo”
    deploy.user = ‘demouser’
    deploy.hosts = ['servergeek.com']
    deploy.path = ‘/home/apps’
    deploy.ssh_opts = ‘-p 3987′

    I get

    Inploy => git push ssh://[demouser@servergeek.com]/home/apps/demo master

    So it is wrong!! it must be:
    Inploy => git push ssh://[demouser@servergeek.com:3987]/home/apps/demo master

    the parameter deploy.ssh_opts = ‘-p 3987′ works in Inploy => ssh -p 3987 demouser@servergeek.com ok

    it is a bug.

    so i set:
    deploy.hosts = ['servergeek.com:3987']
    i get
    Inploy => ssh -p 3987 railsapps@avizormedia.com:3987 wrong

    Solve?

    Dennis

  13. Hi Dennis, this doesn’t resolve? :

    deploy.template = :locaweb
    deploy.application = “demo”
    deploy.user = ‘demouser’
    deploy.hosts = ['servergeek.com:3987']
    deploy.path = ‘/home/apps’

    Im just set the port without setting the ssh option.

  14. Hi dc.rec1
    How I said.
    deploy.hosts = ['servergeek.com:3987']
    i get
    Inploy => ssh railsapps@servergeek:3987 this line is wrong
    Inploy => git push ssh://[demouser@servergeek.com:3987]/home/apps/demo master this line is ok

    IF I set:
    deploy.hosts = ['servergeek.com']
    deploy.ssh_opts = ‘-p 3987′
    i get:
    Inploy => ssh -p 3987 railsapps@servergeek this line is ok
    Inploy => git push ssh://[demouser@servergeek.com]/home/apps/demo master this line is wrong

    AND If I set:
    deploy.hosts = ['servergeek.com:3987']
    deploy.ssh_opts = ‘-p 3987′
    i get:
    Inploy => ssh -p 3987 railsapps@servergeek:3987 this line is wrong
    Inploy => git push ssh://[demouser@servergeek.com:3987]/home/apps/demo master this line is ok

    my server is setup with port 3987

    Dennis

  15. Oh I get it, I thought this was ok:

    ssh railsapps@servergeek:3987

    I will try to implement this functionality. I can also accept a patch if you want :)

    Thanks for the feedback.

  16. Hi Dc.

    I try the plugin again. it’s works very good, very easy.

    but now I see this line:

    Inploy => ssh -p 3987 -p 3987 demouser@servergeek.com ‘cd /home/rails/example && git checkout -f && rake inploy:local:update environment=production’
    with this setup:
    deploy.template = :locaweb
    deploy.application = “example”
    deploy.user = ‘demouser’
    deploy.hosts = ['servergeek.com']
    deploy.path = ‘/home/rails’
    deploy.port = “3987″
    # optional advanced settings
    deploy.ssh_opts = ‘-p 3568′
    #deploy.branch = ‘master’

    and works. only i dont like -p 9999 twice in ssh line

    if I set deploy.ssh_opts = ‘-p 3568′ its write ssh -p -p 3987 demouser@servergeek.com………
    if I comment #deploy.ssh_opts = ‘-p 3568′ it’s write ssh demouser@servergeek.com……… and the port????

    Dennis

  17. Hi Jess, I can implement support for other version control system if you need it. Im trying to keep Inploy as simple as needed, adding functionalities only when there is a demand.

  18. This is really good, but one of the things I really hate about Capistrano is that it doesn’t let you specify different user/pwd for every environment, for instance, I don’t know why but in the company I’m working for right now there is 1 server for each environment, so development and production have different IP and different user/pwd pairs, as far as I understand Inploy doesn’t let you specify different user/pwd either, am I wrong?

  19. Hi Raf,

    Inploy’s config file is pure Ruby, so you can do things like this:

    if ENV['RAILS_ENV'] = ‘production’
    user = ‘batman’
    password = ‘robin’
    else
    user = ‘lorem’
    password = ‘ipsum’
    end

    Hope that helps.

  20. Thank you for the fantastic piece of information. Gives a great insight. Thanks Gautam T Goudar, Irving, TX f

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">