Skip to content

Instantly share code, notes, and snippets.

@bdeterling
Created December 22, 2010 16:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bdeterling/751703 to your computer and use it in GitHub Desktop.
Save bdeterling/751703 to your computer and use it in GitHub Desktop.
Instructions I used to set up a new Ubuntu server with rvm, passenger, rails, etc
These instructions have two purposes: 1) my own notes so I can more easily set up a new machine next time; 2) help for a Rails n00b to get a machine and a project set up without having to make all the same mistakes I did.
This walks you through setting up a remote Linux server and creating a Rails 3 project locally, using Github and Capistrano to collaborate and deploy.
Local -> means on your machine which should be a Unix variant (some OS X specifics may have snuck in)
Server -> means the server
Local ->
setup a /etc/hosts entry to map the IP to a simple name, hereafter 'server'
ssh root@server <enter root password>
-- Add user and set up for ssh public/private key login --
Server ->
adduser you
# answer various prompts
su - you
mkdir .ssh
cd .ssh
create authorized_keys file
if you don't have an existing key (~/.ssh/id_rsa and id_rsa.pub)
Local ->
ssh-keygen -t rsa -C "ssh key for your-name"
it will create ~/.ssh/id_rsa and id_rsa.pub
end
copy contents of local id_rsa.pub to server .ssh/authorized_keys
chmod 600 authorized_keys
cd ..
chmod 700 .ssh
Local ->
attempt ssh server-name (or ssh login@server-name if not the same)
if login is different, create .ssh/config add these lines
Host server
user user-on-server
create one section for each way you may log in, i.e. IP address, official host name, your /etc/hosts entry
-- Add user to sudoers so we can disable root login --
Server ->
su - <enter root password>
vi /etc/sudoers
copy ROOT...all line and change to <username>
-- Test sudo
Server ->
log out and back in
sudo su - <enter your password>
should get root prompt
-- Disable root login --
Server ->
sudo vi /etc/ssh/sshd_config
set PermitRootLogin to no
set PasswordAuthentication to no # only allow ssh keys
ps -ef|grep sshd
kill -HUP <process id of sshd>
-- Set up other users as needed and add to sudoers
-- Install Shorewall (firewall)
# You could use iptables as the firewall, but I don't understand it and Shorewall has a nice
# web admin interface.
@ Using instructions from: http://www.wowtutorial.org/tutorial/16.html
Server ->
sudo apt-get install shorewall
sudo vi /etc/default/shorewall
set startup = 1
*sudo cp /usr/share/doc/shorewall/default-config/* /etc/shorewall/
* I actually copied this from another server since it had the rules I wanted already
-- Install Webmin to make admin of Shorewall less of a pain
# Webmin is the aforementioned web interface for Shorewall and lots of other Linux tools
@ Using instructions from: http://www.ubuntugeek.com/webmin-installation-and-configuration-in-ubuntu-linux.html
Server ->
sudo apt-get install perl5 libnet-ssleay-perl
# look up the latest version of webmin if this is way in the future
cd /tmp
wget http://prdownloads.sourceforge.net/webadmin/webmin-1.510.tar.gz
tar xvfz webmin-1.510.tar.gz
cd webmin-1.510
sudo sh setup.sh
defaults except:
port: 10184 # this can be anything; changed from default for obscurity
user: tadmin # also can be anything; changed from default
password: <same as root>
use ssl: y
start at boot: y
Browser ->
Go to https://server:10184/ (assuming you mapped server to the IP, otherwise use IP)
log in
Networking -> Shorewall Firewall
Zones
Add Zone
id: net
type: ipv4
leave rest blank
Interfaces
Add interface
Interface: eth0
Zone: net
Broadcast: Automatic
uses dhcp, reject packets on blacklist, check for illegal tcp,
log w/impossible sources, check for broadcast source
Default policy
Add
$FW net ACCEPT
net $FW DROP info
net all DROP info
all all REJECT info
Stopped (allow various machines I can get to access when the firewall is stopped; everything else will be blocked)
eth0 99.0.0.1
Firewall Rules
# It's kind of tedious to do this through the UI
# Instead you can edit /etc/shorewall/rules
*** But run check firewall in the browser after editing to make sure you didn't screw it up
Server ->
Contents of rules (minus some comments at top):
# Drop Ping from the "bad" net zone.. and prevent your log from being flooded..
Ping/ACCEPT net:99.0.0.1 all # allow you home to ping
# Permit all ICMP traffic FROM the firewall TO the net zone
Ping/DROP net $FW
ACCEPT $FW net icmp # ping from inside
SSH/ACCEPT net:99.0.0.1 fw # home ssh
ACCEPT net:99.0.0.1 fw tcp 10878 # home webmin
Web/ACCEPT all all # everyone can hit port 80
ACCEPT all all tcp 3000,4567 # everyone can hit rails, sinatra defaults
... add rules for other IP addresses as needed
Browser ->
Run check firewall from webmin
!!! make sure you're still logged in via ssh at this point in case something is screwed up !!!
try ssh from various hosts to makes sure they're allowed
Start firewall
try ssh from allowed hosts in separate window
try more webmin pages to make sure that's allowed
try ssh from other hosts to makes sure they're blocked
Server ->
-- Install MySQL
sudo apt-get install mysql-server
# left root password blank for now
** Would highly recommend not opening up MySQL port via firewall; set root password if you do
-- Install apache2
# There is an Apache module called Phusion Passenger that is the defacto standard for serving
# Rails applications.
sudo apt-get install apache2
-- Install git
sudo apt-get install git-core
-- Create a deploy user
# This will be used by the deploy scripts so we don't have to use our own user names and passwords
sudo adduser deploy
... answer questions
-- Install RVM system-wide
# RVM is a way to easily run multiple versions of Ruby and to isolate gems within sets so
# you can keep track of what your using.
# You could install it either locally for the deploy user or system-wide so all users can
# access it. I chose system-wide.
sudo bash < <( curl -L http://bit.ly/rvm-install-system-wide )
-- Set up rvm for users
# RVM uses an rvm group to control permissions; it's nice to make this the primary group of
# all the users so everyone can read/write stuff in the common directories without having to
# switch all the time.
get id of rvm group from /etc/group
change /etc/passwd so everyone's primary group is rvm
Add to everyone's .profile:
[[ -s "/usr/local/lib/rvm" ]] && . "/usr/local/lib/rvm" # This loads RVM into a shell session.
log out and back in for profile and group to take effect
-- Install Ruby
# 1.9.2 is the way to go for Rails 3.
# Unless otherwise noted, I think this can be done as non-root. Better to try that and fail than to create
# some weird root dependency that you don't find out about until later.
-- Install gcc
* Needed for Ruby 1.9
sudo apt-get install gcc
-- Install other Ruby dependencies
aptitude install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf
rvm install 1.9.2-p0
rvm --default use 1.9.2 # seems to make this the default for all users
-- Install Passenger
# As mentioned, this is an Apache module that groks Rails.
rvm use 1.9.2
gem install passenger
/usr/local/rvm/gems/ruby-1.9.2-p0/bin/passenger-install-apache2-module
# This will configure Passenger; read what it says in case there are dependencies
# unique to your situation
Install missing dependencies listed by above:
sudo apt-get install libcurl4-openssl-dev
sudo apt-get install apache2-prefork-dev
sudo apt-get install libapr1-dev
sudo apt-get install libaprutil1-dev
Re-run after fixing missing dependencies:
/usr/local/rvm/gems/ruby-1.9.2-p0/bin/passenger-install-apache2-module
# Per instructions from passenger (kinda), create /etc/apache2/mods-available/passenger.load:
# This configures the Passenger module.
LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.2-p0/gems/passenger-3.0.2/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p0/gems/passenger-3.0.2
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.2-p0/ruby
cd /etc/apache2/mods-enabled
# The general idea is that there is an available and an enabled directory for modules and sites.
# You put everything in available and then link the stuff into enabled that you want to use.
ln -s ../mods-available/passenger.load passenger.load
sudo /etc/init.d/apache2 restart
Browser -> can double-check non-Rails by going to http://server. Should get default Apache It Worked! page
-- Setup web apps
# There are lots of structures you could use but this one is in some tutorials and seems to make
# sense. /webapps is where the actual apps go; /websites contains links to the appropriate directories
# in the apps; Passenger only knows about /websites although somehow it can still access the others
sudo mkdir /websites
sudo chgrp rvm /websites
sudo mkdir /webapps
sudo chgrp rvm /webapps
sudo chmod 775 /web* # allow rvm group to write
-- Install a test Sinatra app
# Not strictly required, but good for future use and to validate the Passenger configuration without
# introducing Rails into the picture yet.
gem install sinatra
# I use the same structure for Sinatra as for Rails. Under the main directory (sinatra_test) are
# directories for releases with the most recent release being linked to current which is what
# Passenger actually uses. You keep a few recent releases so you can go back just by changing the
# symlink.
mkdir -p /webapps/sinatra_test/current/public
# Passenger deals with 'rack' applications. That's a standard interface for webapps that are generally
# in Ruby but don't have to be. You can actually create a web server in 3 lines of Ruby code that
# conforms to the Rack standard.
# Both Rails and Sinatra follow the rack standard which is why Passenger can treat them the same.
# The config.ru file is the entry into the rack app.
edit /webapps/sinatra_test/current/config.ru:
# before 1.9 you needed "require 'rubygems'" here
# as of 1.9, ./ is required and require_relative doesn't work for some reason
require './sinatra_test'
run Sinatra::Application
edit /webapps/sinatra_test/current/sinatra_test.rb
require 'sinatra'
# On prior versions I had to add these lines for some reason, but
# doesn't seem to matter now
#before do
# request.env['PATH_INFO'] = '/' if request.env['PATH_INFO'].empty?
#end
get '/' do
"Hello there world!"
end
# Link the public directory to /websites for Passenger
ln -s /webapps/sinatra_test/current/public /websites/sinatra_test
# There are lots of options for setting up Apache vhosts with Passenger.
# Normally you would create a sites-available/vhost for every vhost, like
# test.domain.com, stage.domain.com, etc. If there is no domain, everything
# goes through the IP so I think it can only handle one vhost. But you can
# use suburis to map apps like http://server/app1, http://server/app2.
-- Setup Passenger to know about the app
# Most of this will need to be done as root
cd /etc/apache2/sites-available
edit 'apps'
<VirtualHost *:80>
ServerName <ip address of server>
DocumentRoot /websites
<Directory /websites>
Options -Indexes FollowSymLinks MultiViews
AllowOverride None
Allow from all
</Directory>
RackBaseURI /sinatra_test
<Directory /websites/sinatra_test>
Options -MultiViews
</Directory>
</VirtualHost>
cd ../sites-enabled
remove 000-default or whatever it is
* the sites-available copy will still be there
sudo ln -s ../sites-available/apps 001-apps
# the 001 is for when you have multiple vhosts, you can control
# the order of preference
# restart apache
/etc/init.d/apache2 restart
or
sudo apachectl restart
go to http://server/sinatra_test
# Should see hello there world or whatever
-- Setup Mail
# Used webmin for this
Go to unused modules
Choose postfix
Prompts you to install it
Didn't really change any configuration
- did change to only allow smtp from server (rather than network)
- also turned on sasl something or other
Added Shorewall firewall rule to allow smtp from <firewall> to <any>
* Not sure this was needed; think firewall -> any is open by default
# Use mailx for testing
sudo apt-get install mailutils
mailx some email address you own
# see if you got it
# Had problems with sending from Rails; I think it's due to no valid domain name
# Options are gmail, sendgrid, bluesky, etc
-- Setup Github
# Information on using git and Github.
# There is some first-time setup stuff here that you should do like configuring
# your name and e-mail; I left it out below.
http://help.github.com/creating-a-repo/
http://toolmantim.com/thoughts/setting_up_a_new_rails_app_with_git
On Github ->
Create repository <myproject> # hereafter just myproject
Add collaborators as needed
Local ->
In local projects directory (I use ~/projects)
# This assumes you have installed rvm and ruby 1.9.2 locally
rvm use 1.9.2
# A gemset is a way of isolating all the dependencies for a project. Before they existed,
# you would end up with 100 gems installed system wide and would not know which ones went
# with which project. Now you can install gems per-project so you know what you're using.
rvm gemset create myproject
rvm use 1.9.2@myproject
# create an empty Rails project
rails new myproject
cd myproject
# It's hard to remember to set your gemset when working multiple projects. .rvmrc lets
# you have it automatically set it when you cd into your project directory.
edit .rvmrc to say 1.9.2@myproject
# Stage all of the Rails project. Rails is smart enough to add stuff like logs to .gitignore
# which keeps it from being checked in. This does not actually commit the files, only
# moves them to the staging directory.
git add .
# Now actually commit stuff. It's still only in your local repository though.
git commit -m "First commit"
# Set up the remote (central) repository that will be shared
# Substitute your github username
git remote add origin git@github.com:<your-github-username>/myproject.git
# Actually push your commits to the remote repository. origin is the name of the
# remote (it could be anything but that's the standard). master is the default branch.
# For serious multi-user projects you will want to learn about branching and
# rebasing. Terrible stuff in svn, but a major benefit of git.
git push origin master
# A gem is more or less like a jar file in Java (think apache-commons-lang.jar), but
# includes information about where the source is, what it depends on, etc.
# Gemfile is a list of your project's dependencies. This is a relatively new concept
# based on the bundler packaging tool. Before bundler, you had to manually load gems
# and set up the same versions on your various machines. Devs could have different
# versions which could cause problems.
edit Gemfile
uncomment mysql # the database gem
uncomment capistrano # used for deploying easily
uncomment ruby-debug19 # the ruby debugger
Add these (not required, but might be useful in the future):
gem 'foreigner' # commands to set foreign keys in migrations
gem 'dynamic_form' # this used to be built-in, e.g. f.error_messages
gem 'jquery-rails' # by default Rails uses prototype but JQuery is better
# Load new gems and update Gemfile.lock which includes all the dependencies
bundle update
# Rails by default uses Prototype, but JQuery has become more popular.
# This will install various JQuery components and change the rails.js file
# to use JQuery instead of Prototype.
rails generate jquery:install --ui
# Instead of using git add/commit/push, I normally use git-gui. It pops up
# a window that lets you see everything that changed since the last commit.
# Select the files in top left, cmd-t to stage them, then enter a comment and
# commit. Then push to send them to the remote server.
stage, commit, and push your Gemfile changes
-- Set up databases
edit config/database.yml; change to use mysql
add stage environment so you should have dev, test, stage, and production
e.g.
production:
adapter: mysql
database: myproject_prod
username: root
password:
host: localhost
pool: 5 # skip this for non-prod
timeout: 5000 # skip this for non-prod
# commit and push changes
Server ->
mysql -u root
create database myproject_stage
create database myproject_prod
-- Setup deploy process
# Capistrano is a gem that lets you automate a lot of tasks on a remote server. It's nice
# enough that you can automate it for one server, but once you have it set up, you can
# just add more servers to the list and it will deploy to all of them, e.g. if you use
# load balancing. It works by ssh'ing into the server and running the commands.
@ Using:
http://help.github.com/capistrano/
http://railstips.org/blog/archives/2008/12/14/deploying-rails-on-dreamhost-with-passenger/
-- Setup deploy user on server for Github access
Server ->
# Github lets you create a deply ssh key so you don't have to use passwords
# to grab the latest versions.
sudo su - deploy
ssh-keygen -t rsa -C "deploy key for myproject"
default directory
passphrase same as deploy user (or whatever)
# So anyone can deploy but it will run as the deploy user:
add other user's public keys to ~deploy/.ssh/authorized_keys
On Github ->
add deploy key (public, i.e. id_rsa.pub) to github repository under admin -> deploy keys
Server ->
chown deploy:rvm authorized_keys (if not done earlier)
chmod 600 authorized_keys (if not done earlier)
ssh git@github.com
enter passphrase
should get message saying it worked
Local ->
# test logging in as deploy user
ssh deploy@server
Server ->
# Install bundler in base 1.9.2 install on server
rvm use 1.9.2
gem install bundler
# Setup gemset to match local
rvm gemset create myproject
# Technically this wasn't necessary but I got errors on a normal
# deploy because it couldn't install the debugger, so I installed
# it as root ahead of time. You could change Gemfile to only use
# debugger or development and test instead.
as root:
rvm 1.9.2@myproject
gem install ruby-debug19
Local ->
# Add settings for stage environment. We'll use stage to test stuff on
# the server before deploying to production.
cp config/environments/production.rb config/environments/stage.rb
# change local requests to true. When false, it gives a generic clean
# error message; when true, it gives a stacktrace.
# Set up the project to use Capistrano
capify .
# Update lots of stuff in config/deploy.rb. Too much to document, just
# copy the existing one in the future.
# Important things are:
* use stage as default
* make sure rvm_type is system, not user since we're doing a systemwide rvm
# Use cap to set up the directory structure on the server. Given the config/deploy.rb
# file this will set up /webapps/myproject/stage/releases, current, etc
cap deploy:setup
# Validate everything
cap deploy:check
# Do the first deploy
cap deploy
# Assuming stage worked, set up production
RAILS_ENV=production cap deploy:setup # same thing but for production
RAILS_ENV=production cap deploy
# Checkin deploy changes (for other users, deploying only cares about code that's checked in)
git commit -a -m "Deploy stuff"
git push
-- Setup Rails apps with Passenger
Server ->
As deploy user:
# Link the apps to the /websites directory for Passenger
cd /websites
ln -s /webapps/myproject/stage/current/public myproject_stage
ln -s /webapps/myproject/production/current/public myproject
As root:
* This assumes you set up the Sinatra test app. If not, follow those instructions
* for the stuff in /etc/apache2.
cd /etc/apache2/sites-enabled
edit 001-apps add the following inside the <VirtualHost> tags
RailsBaseURI /myproject_stage
<Directory /websites/myproject_stage>
Options -MultiViews
</Directory>
RailsBaseURI /myproject
<Directory /websites/myproject>
Options -MultiViews
</Directory>
cd /webapps/myproject/stage/current
# should prompt to use .rvmrc the first time
bundle install
# This should already be done but for some reason this triggers passenger to use the right gemset
# Do the same for production:
cd /webapps/myproject/production/current
bundle install
# Restart apache
apachectl restart
# Try http://server/myproject_stage and http://server/myproject
-- Making changes (no database changes)
Local ->
# Make your changes
rails s to run the server and test (http://localhost:3000)
Commit and push
cap deploy
# Test stage
RAILS_ENV=production cap deploy
# Test prod
-- Making changes (with database changes)
Local ->
rails generate migration name_of_migration
edit db/migrate/<file that was generated>
add create_table, add_column, etc
# Rake is like make on steroids; it does build type stuff but pretty much
# any other scripted task also.
# Migrate the changes, i.e. alter table, create table, etc
rake db:migrate
# test - should not need a server restart
git commit, push your changes
# Update stage, migrate stage database, deploy
# This can also be done in multiple steps (update_code, migrate, deploy)
cap deploy:migrations
# test stage
# Migrate and deploy production
RAILS_ENV=production cap deploy:migrations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment