Hosting services like Heroku and Amazon EC2 are nice. That is, until they cost money. Some things are worth running on your own hardware, especially when the cost and Terms of Service requirements outweigh the expense of rolling your own hosting.
I am writing this because I recently had to figure all this out in order to host a personal blog off a Raspberry Pi, and I thought I'd share what I learned. This guide assumes that you already know how to install Ruby and you know how to use Rails. If you don't, look those up first before coming back to this guide.
- Ruby >=2.0
- Rails >=4.0
- Nginx
- Debian(or Raspbian)
- A domain name hosted through Amazon Route53
Add these lines:
gem 'whenever', require: false
gem 'aws-sdk', '~> 1', require: false
I chose version 1 of the AWS SDK because there were some existing implementations of my use case for it and it seemed a tad simpler than how it would work with version 2.
If you don't know what an A record is, basically, your home server probably has a dynamic IP address so it changes every once in a while; you need to tell Route53 what your new IP adddress is so that your domain points to your server.
Here's is the task that I placed in the file lib/tasks/update_a_record.rake
:
require 'aws-sdk'
task :update_a_record => [:environment] do
r53 = AWS::Route53.new(
:access_key_id => Rails.application.secrets.aws['access_key_id'],
:secret_access_key => Rails.application.secrets.aws['secret_access_key'])
change = {
:action => 'UPSERT',
:resource_record_set => {
:name => Rails.application.secrets.aws['domain'],
:type => "A",
:ttl => 600,
:resource_records => [{:value => `wget http://ipinfo.io/ip -qO -`}]
}
}
r53.client.change_resource_record_sets({
:hosted_zone_id => "#{Rails.application.secrets.aws['hosted_zone_id']}",
:change_batch => {
:changes => [change]
}
})
end
The different authentication values and my domain name are stored in config/secrets.yml
. If you don't know how to use secrets.yml, the Rails official guides explain it.
My task is pretty simplistic and does a fairly "logicless" update every day at 12am. I've found this to be sufficient because my IP address rarely changes and, since few people read my blog, I really don't care if the IP address is wrong for a while. If you want to go the extra mile, you could change it to check if there's a difference in IP addresses since the last time it ran, and schedule it to run more often.
We are going to use the Whenever gem to write a custom Crontab file that will do two things for us:
- Automatically start our Rails server on reboot
- Periodically update the A record on our domain.
Write something like the following in a new file config/schedule.rb
in your Rails project:
every '0 0 * * *' do
rake "update_a_record", environment: :production
end
every '@reboot' do
command "source /home/pi/.rvm/scripts/rvm && cd /home/pi/blog && bundle exec rails s -b 0.0.0.0 -p 3000 -e production -d &"
end
You will need to edit the line in the @reboot block to reflect the path where your Rails app is located on your server. source /home/pi/.rvm/scripts/rvm
is required if you are using RVM.
The reason we are using Cron to autostart our server is because I've found it a pain in the ass to get it to work in root and all that. This is just way easier this way and it gets stored directly in the Rails project as opposed to some separate file in the Linux directory structure.
Once you have this written, execute whenever -w
in your terminal and you're done.
Inside your /etc/nginx/nginx.conf
file(thought it may be located elsewhere), make sure this line is in the http section:
include /etc/nginx/sites-enabled/*;
Then create the file /etc/nginx/sites-enabled/default
. Fill in the path to your Rails project.
upstream rails_server {
server 0.0.0.0:3000;
}
server {
listen 80;
location / {
root [PATH TO YOU RAILS PROJECT]/public;
try_files $uri @missing;
}
location @missing {
proxy_pass http://rails_server;
proxy_set_header Host $http_host;
proxy_redirect off;
}
}
The reason we are using Nginx is so that we can run the Rails server on port 3000 and it gets routed to port 80 without the need to run our project as root. Nginx also has good logging, and it opens up the possibility of running multiple servers running under the same IP address but different domains.
Let's set Rails to run in Production by default. Add the following line to the top of your ~/.bashrc
file:
export RAILS_ENV=production
Lastly, run rake update_a_record
to update your domain to point to your server's IP address.
Then restart your server, and your app should be up and running on your chosen domain name.
Hi, thank you for the detailed guide! Do you know if it is possible to get https security while hosting on a home server? Thanks.