How can I deploy a Rails app to a VPS (virtual private server) or a dedicated server? It would be nice to have an easy to follow guide.
I know about scripts to automate the process, but I think it's better to have everything under control, in order to understand better the process.
I have successfully deployed a heavy Rails application to Linode or Digital Ocean, using these technologies:
- rbenv for Ruby installation
- nginx + Passenger for the application server
- PostgreSQL for the database server
- Capistrano to automate deploys (configure this first on your dev machine with your server IP and settings, I will not cover it here)
These are the steps that work for me:
Setting up the virtual machine
Create a new virtual machine
Follow the setup instructions of your hosting, being Linode or Digital Ocean, to create the node and set it up.
Set up date
Update packages
- apt-get update
- apt-get upgrade
Security
Create user
- adduser deploy
- usermod -a -G sudo deploy
- logout
Set up SSH key-authentication
On local:
- ssh-keygen
- copy the public key:
- scp ~/.ssh/id_rsa.pub deploy@example.com:~
On the server:
- ssh deploy@example.com
- enable the alias to list files:
- vim ~/.bashrc
- uncomment all aliases
- mkdir .ssh
- mv id_rsa.pub .ssh/authorized_keys
- chown -R deploy:deploy .ssh
- chmod 700 .ssh
- chmod 600 .ssh/authorized_keys
- logout (test the new authentication)
Set up SSH
- sudo vim /etc/ssh/sshd_config
- Switch PermitRootLogin to no
- sudo service ssh restart
Set up firewall
- sudo iptables -L (it should show a void table)
- sudo vim /etc/iptables.firewall.rules
- Paste this: https://gist.github.com/davidmles/89fc88e48e17cf8252bfca374e46355f#file-iptables-firewall-rules
- sudo iptables-restore < /etc/iptables.firewall.rules
- sudo iptables -L (now it should show the configured rules)
- sudo vim /etc/network/if-pre-up.d/firewall
- Paste this: https://gist.github.com/davidmles/89fc88e48e17cf8252bfca374e46355f#file-firewall
- sudo chmod +x /etc/network/if-pre-up.d/firewall
Set up fail2ban
Set up if you have enough free memory, as it tends to eat it.
- sudo apt-get install -y fail2ban
Setup Ruby
Install Git
- sudo apt-get install -y git
Install rbenv
- git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
- echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
- echo 'eval "$(rbenv init -)"' >> ~/.bashrc
- source ~/.bashrc
- git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
Install Ruby
- sudo apt-get install -y curl gnupg build-essential
- rbenv install -l (look for the latest version)
- rbenv install 2.3.3 (or the latest available version at this moment)
- rbenv global 2.3.3
- rbenv rehash
- vim .gemrc
- Paste this: gem: --no-document
Setup servers
Install nginx + Passenger
- Install following the documentation:
- https://www.phusionpassenger.com/library/install/nginx/install/oss/jessie/
Install PostgreSQL
- Install following the documentation:
- https://wiki.postgresql.org/wiki/Apt#PostgreSQL_packages_for_Debian_and_Ubuntu
Setup libraries
Install node.js
Needed to pre-compile assets.
- sudo apt-get install -y nodejs
Install bundler
- get install bundler
- rbenv rehash
Setup the application
Create the user in PostgreSQL
- createuser username --pwprompt
- createdb -Ousername -Eutf8 db_name
- Test it:
- psql db_name --user username --password
Deploy the code
* On the server:
* sudo mkdir -p /srv/yoursite.com
* sudo chown deploy:deploy /srv/yoursite.com
* On your dev machine:
* bundle exec cap production deploy:check (it will throw an error because it doesn't find the database)
* On the server:
* cd /srv/yoursite.com/shared/config
* vim database.yml (paste your database config)
* vim secrets.yml (paste your secrets config)
* On your dev machine:
* bundle exec cap production deploy
* bundle exec cap production whenever:update_crontab
Configure logrotate
- Follow this guide: * https://gorails.com/guides/rotating-rails-production-logs-with-logrotate
I deployed my rails application to my production servers (it's a cluster) with Capistrano before, but I found that Capistrano is a bit complex and sometimes even became trouble maker... So I wrote my deployment script by bash shell script.
I have put it on github with a brief guide: deploy_rails