50 Responses to “Deployment Recipes – Deploying, monitoring and securing your Rails application to a clean Ubuntu 10.04 install using Nginx and Unicorn”
I don’t really see any reason to use RVM in a production environment, I’d rather go for the distribution available Ruby, as they have usually been tried and tested with the other dependencies in the system and should behave correctly. I do use RVM on my dev machine and it’s a basic tool for any Ruby developer, but I like the hardened feel of the packaged ruby for my production websites.
But that’s a matter of opinion, if you find that RVM isn’t an issue for you, go on then, it’s just a single dependency to remove from the apt-get call :)
Any specific reason why they’re terrible, Randuin?
Most of the times I had segfaults in production i was using a compiled or “not distributed” Ruby, so now I’m a bit more conservative about the interpreter I’m running on my machines.
The benefit to RVM isn’t just the Ruby binaries: there’s been some great strides in giving each separate application its own gemset, which allows a clean environment for each app to run. This, alone, is worth the minimal effort of setting up RVM.
That said, RVM also makes it incredibly simple to upgrade/change Ruby versions, and even allows you (through the use of .rvmrc files) to automatically switch to that version of Ruby and gemset when entering each project’s directory. It’s a fantastic tool.
it’s quite useful to use gemsets on different production apps, such as monit/god etc.
Or for when you want to update the ruby, they can run side by side and you have a method to revert. Doing upgrades in production without a way to revert is asking for trouble!
I’ve been learning RoR this week and this tutorial has really been helpful in bringing everything together as I try to put some of my scripts on my live server. It’s also a good little intro to Git, where I plan on putting some open source stuff I’ve worked on in other languages.
Your nginx config for virtual host contains errors. First off – access_log accepts path value OR `off’ value. There is no such value as `on’. That’s why you write this:
—
And finally, as root, create the following directory:
mkdir /usr/local/nginx
—
, but do not actually explain why this is needed. If you supply decent path for `access_log’ directive – you won’t need that directory.
If you used real upstart scripts instead of its sysv compatibility layer, you could skip monit entirely.
That said monit offers more ways to check a process/service. upstart by default just checks if a process is alive, not what a webserver for example actual delivers.
From my local benchmarks Unicorn is being faster than Passenger and being able to run it outside of the webserver also makes it easier to configure and monitor. It also allows you to “update” your codebase and restart everything without downtime, as the old Unicorn instances are only going to be turned off once the new ones start to rise, I’m not sure how passenger handles that or if the latest version fixes this issue.
Hi Maurício,
I followed your tutorial and I think I am almost there but when I access the domain, I get 500 Internal Server error and the nginx error log file says
2010/10/13 05:18:08 [alert] 2577#0: *1015 socket() failed (24: Too many open files) while connecting to upstream, client: , server: localhost, request: “GET / HTTP/1.0”, upstream: “http://:80/”, host: “”
nginx and unicorn seem to be running fine. Don’t where else to look.
I tried googling but couldn’t find any answers. I am clueless, Could you please offer any suggestions?
Hi Maurício,
I am finding your tutorial very useful, deploying my second one here.
Can you give some guidance on what the nginx config changes I have to make to serve multiple domains on the same host.
You kinda mentioned it to create separate file in sites-available folder and I created one called domain2.com and then symlinked it to sites-enabled folder but then I am stuck.
Should I rename the default file to domain1.com?
Should I change the nginx.conf file at all ?
You should not change the default file, you should create a new sile inside the sites-available folder, here’s a simple example of how you would do it if you had to answer requests to a “somedomain.com” domain:
First thing is create a “somedomain.com.conf” file inside “/etc/nginx/sites-available/”, the name doesn’t need to be exactly like this, but I myself like to use the domain names as the name of the virtual server config files, you could name it “somedomain.conf” and it would work the same way.
With this file created, you must configure the host this server is going to answer requests:
server {
# if you’re running multiple servers, instead of “default” you should
# put your main domain name here
listen 80 somedomain.com;
# you could put a list of other domain names this application answers
server_name http://www.somedomain.com;
#add the rest of your server config, including proxies after this line
}
Now, with this file created and configured correctly with the proxy config, you have to tell nginx to load it, you do this by symlinking this file to the /etc/nginx/sites-enabled folder, here’s the command:
Thank you for your prompt reply.
I did the changes you mentioned and when I restart nginx, I get this error message:
Restarting nginx: [warn]: the “user” directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:1
[emerg]: “domain2.com” parameter can be specified for the default “listen” directive only in /etc/nginx/sites-enabled/domain2.com.conf:16
configuration file /etc/nginx/nginx.conf test failed
Any ideas what it is complaining about?
nginx.conf and default files remain untouched, correct ?
Hi Mauricio! Thanks for great tutorial!!!
I have a little problem with cap deploy:cold, no such file to load — initializer.
I’m new in RoR and don’t know how to fix this problem, can you help?
thanks
Cos right now I’m still building the text material for the Objective-C/iOS dev course, it’s taking a lot longer than I expected :(
And unfortunately, the material is being written in portuguese, given it’s going to be used on a iOS dev course in Brazil and at this moment I don’t have plans to translate it to english, but I’ll probably post here as soon as the first batch of work si ready.
Sim, inicialmente o material vai ser apresentado no Maré, mas ele vai ser expandido pra virar um curso mesmo que deve estar disponível pela e-Genial a partir do início do ano que vem, estou trabalhando pra produzir todo o material de referência do curso e deixar tudo prontinho pra dar andamento a isso.
The only differences is that you would be installing ruby9 and mysql2 instead of the plain ruby 1.8 i’m installing with ruby-full, everything else should be the same.
Is it a Rails error or something nginx is returning? If it’s a Rails error, you should probably try to run a script/server in your server to be sure that everything is running fine.
I try to run a rails s in my server, and the it’s successfully run, no errors. i try to upload documents and video to my web-site, in nging conf I add the location for my documents and video. In edit form i can upload documents and video file, this files are uploading to the server, but for some reason, the browser can’t see it and throw me 404 error, which i can find in my log file
Started GET “/document/file/6/Vestnik.pdf” for 46.73.156.144 at
ActionController::RoutingError (No route matches “/document/file/6/Vestnik.pdf”):
Ok, that’s rails telling us that there’s no file in that path. Are you using a gem or anything like that to handle your uploads?
If this file is really available, it should be at “#{RAILS_ROOT}/public/document/file/6/Vestnik.pdf”, which isn’t really nice. Uploaded files should always be at the “#{RAILS_ROOT}/public/system” folder, that’s symlinked between deployments. All your uploaded files should either be in an external service, like Amazon S3 or inside the public/system folder.
Neither Rails nor Nginx is being able to see and serve this file, even if it looks that the file is at the right place. Could be a permissions issue, but it’s rather unlikely.
August 25, 2010 at 6:05 pm
please please please use rvm instead of a vanilla ubuntu ruby install. It makes upgrading rubies and managing gems SOOO much easier.
August 25, 2010 at 6:13 pm
Hey Nick,
I don’t really see any reason to use RVM in a production environment, I’d rather go for the distribution available Ruby, as they have usually been tried and tested with the other dependencies in the system and should behave correctly. I do use RVM on my dev machine and it’s a basic tool for any Ruby developer, but I like the hardened feel of the packaged ruby for my production websites.
But that’s a matter of opinion, if you find that RVM isn’t an issue for you, go on then, it’s just a single dependency to remove from the apt-get call :)
August 25, 2010 at 9:51 pm
The distribution rubies are terrible. Especially if you’re deploying rails 3 apps.
August 26, 2010 at 12:59 am
Any specific reason why they’re terrible, Randuin?
Most of the times I had segfaults in production i was using a compiled or “not distributed” Ruby, so now I’m a bit more conservative about the interpreter I’m running on my machines.
August 26, 2010 at 1:29 am
The benefit to RVM isn’t just the Ruby binaries: there’s been some great strides in giving each separate application its own gemset, which allows a clean environment for each app to run. This, alone, is worth the minimal effort of setting up RVM.
That said, RVM also makes it incredibly simple to upgrade/change Ruby versions, and even allows you (through the use of .rvmrc files) to automatically switch to that version of Ruby and gemset when entering each project’s directory. It’s a fantastic tool.
August 26, 2010 at 1:18 am
it’s quite useful to use gemsets on different production apps, such as monit/god etc.
Or for when you want to update the ruby, they can run side by side and you have a method to revert. Doing upgrades in production without a way to revert is asking for trouble!
Also, ree or 1.9.2 are way better than the stock standard rubies. Ubuntu packages a ruby 1.8.7 compiled with enable-pthread, which makes it terribly slow – see here for details: https://bugs.launchpad.net/ubuntu/+source/ruby1.8/+bug/307462
August 25, 2010 at 9:09 pm
Nice one.
Rvm in a production environment is useful in case you are running rails 2 and rails 3 applications.
August 26, 2010 at 12:58 am
That’s true, i think i’ll add some notes on another blog post on how to get RVM up to run many rubies at the same time :)
September 3, 2010 at 5:56 am
That would be awesome. I’m new to rails deployment and I want to learn how to setup an Ubuntu Server 10.04 with Nginx, Unicorn and RVM.
August 25, 2010 at 9:59 pm
Comipling isn’t true way. Fresh rubygems is in Ubuntu on Rails PPA: https://launchpad.net/~ubuntu-on-rails/+archive/ppa
August 26, 2010 at 12:08 am
Thanks,
I’ve been learning RoR this week and this tutorial has really been helpful in bringing everything together as I try to put some of my scripts on my live server. It’s also a good little intro to Git, where I plan on putting some open source stuff I’ve worked on in other languages.
Reddit <3
August 26, 2010 at 1:31 am
authorized_keys2 is deprecated, and has been since 2001. http://sial.org/howto/openssh/#s6
August 26, 2010 at 10:26 am
[…] Nginx, Unicorn, Ubuntu 10.04, Monit complete deployment guide. […]
August 29, 2010 at 9:04 am
Your nginx config for virtual host contains errors. First off – access_log accepts path value OR `off’ value. There is no such value as `on’. That’s why you write this:
—
And finally, as root, create the following directory:
mkdir /usr/local/nginx
—
, but do not actually explain why this is needed. If you supply decent path for `access_log’ directive – you won’t need that directory.
August 29, 2010 at 1:33 pm
Thanks Eimantas, issue fixed now :)
August 30, 2010 at 3:30 am
Hey Mauricio
Saw this post via the ruby inside weekly mail – nice one!
Thanks for the reference material!
September 2, 2010 at 9:04 am
Yeah, this one is a breeze when I remember our old times with Solaris :D
September 2, 2010 at 2:29 pm
If you used real upstart scripts instead of its sysv compatibility layer, you could skip monit entirely.
That said monit offers more ways to check a process/service. upstart by default just checks if a process is alive, not what a webserver for example actual delivers.
September 19, 2010 at 10:39 pm
Thanks for this guide.
Just wondering, what are the benefits of doing this way over using Passenger/nginx?
September 21, 2010 at 3:48 pm
Hi Hari,
From my local benchmarks Unicorn is being faster than Passenger and being able to run it outside of the webserver also makes it easier to configure and monitor. It also allows you to “update” your codebase and restart everything without downtime, as the old Unicorn instances are only going to be turned off once the new ones start to rise, I’m not sure how passenger handles that or if the latest version fixes this issue.
October 13, 2010 at 2:22 am
Hi Maurício,
I followed your tutorial and I think I am almost there but when I access the domain, I get 500 Internal Server error and the nginx error log file says
2010/10/13 05:18:08 [alert] 2577#0: *1015 socket() failed (24: Too many open files) while connecting to upstream, client: , server: localhost, request: “GET / HTTP/1.0”, upstream: “http://:80/”, host: “”
nginx and unicorn seem to be running fine. Don’t where else to look.
I tried googling but couldn’t find any answers. I am clueless, Could you please offer any suggestions?
Thanks for your help,
Hari
October 13, 2010 at 4:43 am
Fixed it!
It came down to this:
My app name is different from the domain name
In the sites-available/default file, for
upstream shop {
I had replaced it with upstream and was seeing those errors, really strange and threw me off.
Changed it to upstream and it started working.
Thanks for the tutorial,
Regards
October 18, 2010 at 5:43 pm
Hi Maurício,
I am finding your tutorial very useful, deploying my second one here.
Can you give some guidance on what the nginx config changes I have to make to serve multiple domains on the same host.
You kinda mentioned it to create separate file in sites-available folder and I created one called domain2.com and then symlinked it to sites-enabled folder but then I am stuck.
Should I rename the default file to domain1.com?
Should I change the nginx.conf file at all ?
Thanks
October 18, 2010 at 8:08 pm
Hi Hari,
You should not change the default file, you should create a new sile inside the sites-available folder, here’s a simple example of how you would do it if you had to answer requests to a “somedomain.com” domain:
First thing is create a “somedomain.com.conf” file inside “/etc/nginx/sites-available/”, the name doesn’t need to be exactly like this, but I myself like to use the domain names as the name of the virtual server config files, you could name it “somedomain.conf” and it would work the same way.
With this file created, you must configure the host this server is going to answer requests:
server {
# if you’re running multiple servers, instead of “default” you should
# put your main domain name here
listen 80 somedomain.com;
# you could put a list of other domain names this application answers
server_name http://www.somedomain.com;
#add the rest of your server config, including proxies after this line
}
Now, with this file created and configured correctly with the proxy config, you have to tell nginx to load it, you do this by symlinking this file to the /etc/nginx/sites-enabled folder, here’s the command:
ln -S /etc/nginx/sites-available/somedomain.com.conf /etc/nginx/sites-enabled/somedomain.com.conf
Now you can restart nginx and this new configurarion should be available.
October 18, 2010 at 9:58 pm
Thank you for your prompt reply.
I did the changes you mentioned and when I restart nginx, I get this error message:
Restarting nginx: [warn]: the “user” directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:1
[emerg]: “domain2.com” parameter can be specified for the default “listen” directive only in /etc/nginx/sites-enabled/domain2.com.conf:16
configuration file /etc/nginx/nginx.conf test failed
Any ideas what it is complaining about?
nginx.conf and default files remain untouched, correct ?
Thank you for your time.
October 19, 2010 at 8:08 pm
Yes, they remain untouched, it’s easier if you paste your file and you should be running nginx as root with a sudo call.
October 22, 2010 at 5:38 pm
Hi Mauricio! Thanks for great tutorial!!!
I have a little problem with cap deploy:cold, no such file to load — initializer.
I’m new in RoR and don’t know how to fix this problem, can you help?
thanks
October 26, 2010 at 9:56 am
Hi George,
Don’t you have a stack trace?
November 2, 2010 at 7:40 am
Hi Mauricio!
Sorry, it was mine mistake… The problem is that my project was created on rails 2, and my server is on rails 3…
thanks
October 26, 2010 at 9:53 am
Hi Mauricio,
Why do you don’t post about development with cocoa and ObjC?
October 26, 2010 at 9:56 am
Cos right now I’m still building the text material for the Objective-C/iOS dev course, it’s taking a lot longer than I expected :(
And unfortunately, the material is being written in portuguese, given it’s going to be used on a iOS dev course in Brazil and at this moment I don’t have plans to translate it to english, but I’ll probably post here as soon as the first batch of work si ready.
October 26, 2010 at 10:35 am
Maurício,
Don’t have problem, I’m a brazilian guy. =]
Se escrever os posts em português não tem problema nenhum.
Esse curso é o que vai ter no Maré de Agilidade, em Fortaleza?
Quando fui me inscrever tinha acabado as vagas. =/
October 26, 2010 at 10:46 am
Sim, inicialmente o material vai ser apresentado no Maré, mas ele vai ser expandido pra virar um curso mesmo que deve estar disponível pela e-Genial a partir do início do ano que vem, estou trabalhando pra produzir todo o material de referência do curso e deixar tudo prontinho pra dar andamento a isso.
October 26, 2010 at 10:49 am
Legal, vou ficar no aguardo do curso. =]
December 3, 2010 at 9:23 am
Hi Mauricio,
Thank you so much for your tutorial.
Can you tells what to change to use ruby 1.9 and mysql2 adapter?
I know this is a basic question but for a newbie like me, it will save me a lot of trouble :)
Vincent
December 3, 2010 at 9:36 am
Hi Vincent,
The only differences is that you would be installing ruby9 and mysql2 instead of the plain ruby 1.8 i’m installing with ruby-full, everything else should be the same.
December 26, 2010 at 2:07 pm
Hi Mauricio,
Can you help me to fix the problem with routes? On my local machine everything’s fine, but on the server i have an error:
The page you were looking for doesn’t exist.
You may have mistyped the address or the page may have moved.
Thanks
December 26, 2010 at 2:56 pm
Hi George,
Is it a Rails error or something nginx is returning? If it’s a Rails error, you should probably try to run a script/server in your server to be sure that everything is running fine.
December 26, 2010 at 4:12 pm
Hi Mauricio,
I try to run a rails s in my server, and the it’s successfully run, no errors. i try to upload documents and video to my web-site, in nging conf I add the location for my documents and video. In edit form i can upload documents and video file, this files are uploading to the server, but for some reason, the browser can’t see it and throw me 404 error, which i can find in my log file
Started GET “/document/file/6/Vestnik.pdf” for 46.73.156.144 at
ActionController::RoutingError (No route matches “/document/file/6/Vestnik.pdf”):
Thanks
December 26, 2010 at 4:17 pm
Ok, that’s rails telling us that there’s no file in that path. Are you using a gem or anything like that to handle your uploads?
If this file is really available, it should be at “#{RAILS_ROOT}/public/document/file/6/Vestnik.pdf”, which isn’t really nice. Uploaded files should always be at the “#{RAILS_ROOT}/public/system” folder, that’s symlinked between deployments. All your uploaded files should either be in an external service, like Amazon S3 or inside the public/system folder.
December 26, 2010 at 4:23 pm
I’m using upload_column plugin for uploading files.
December 26, 2010 at 4:26 pm
Did you check if the file is in there somewhere in your server?
December 26, 2010 at 4:39 pm
no, they are only in my public/document and public/video directory
December 26, 2010 at 4:40 pm
So the files ARE in your server, inside the “document” and “video” folder? is that what you mean?
December 26, 2010 at 4:42 pm
Yes
December 26, 2010 at 4:46 pm
Ok, try this then:
Get into the “RAILS_ROOT/public/document/file/6/” folder, run a “ls -l” and paste the output here.
December 26, 2010 at 4:47 pm
-rw-r–r– 1 deployer staff 12973088 2010-12-26 20:38 Vestnik.pdf
December 26, 2010 at 4:52 pm
I’m out of ideas then.
Neither Rails nor Nginx is being able to see and serve this file, even if it looks that the file is at the right place. Could be a permissions issue, but it’s rather unlikely.
December 26, 2010 at 4:57 pm
So do i)))
whatever Thanks
December 27, 2010 at 1:15 am
You can use some of the techniques for monitoring and managing non-ruby sites i.e asp.net and php.