Skip to content

Instantly share code, notes, and snippets.

@bsaf
Created September 22, 2014 11:38
Show Gist options
  • Save bsaf/96a0dd0dc588e6abc562 to your computer and use it in GitHub Desktop.
Save bsaf/96a0dd0dc588e6abc562 to your computer and use it in GitHub Desktop.
Server setup

Server setup

These are the steps I go through to configure a new server for use with a Rails app.

Provisioning the server

Create the Linode

  • Either create a new one or rebuild an existing one.
  • Select Ubuntu 14 LTS

Create your user on the server

SSH into the server:

ssh root@[IP]

Add a user with the same name as your local user (whoami).

sudo addgroup application
sudo adduser [username] --ingroup application

Don't forget to store your password somewhere.

Enable sudo access for the application group

sudo visudo

Add this rule:

# Members of the group application may gain root privileges
%application ALL=(ALL) ALL

Set the hostname to your domain (example.com)

sudo vi /etc/hostname # Change this to be example.com
sudo vi /etc/hosts    # Append [appname] to first line
sudo reboot

Copy your public key to the server

# On your local machine
ssh-copy-id [local-username]@[ip]

Log onto the server and attempt an ssh to Github/Bitbucket

ssh [username]@[ip]
ssh git@github.com
ssh bitbucket.org

Install Ruby

sudo apt-add-repository -y ppa:brightbox/ruby-ng
sudo apt-get update
sudo apt-get install -y ruby2.1 # Install ruby 2.1.
sudo apt-get install -y ruby2.1-dev  # Also need development headers for some gems

Install Postgres

sudo apt-get install postgresql
sudo apt-get install libxslt-dev
sudo apt-get install libxml2-dev

Install nginx

sudo apt-get install nginx
sudo service nginx start

Install git

sudo apt-get install git

Deploying your app to the server

Add to Gemfile

gem 'recap' # dev group only
gem 'unicorn'
gem 'foreman'

Create the Procfiles

Procfile:

web: unicorn_rails -p 3000

Procfile.production --- replace with your app name:

web:  bundle exec unicorn -c /home/[appname]/app/config/unicorn-production.rb -E production >> /home/[appname]/app/log/web.log 2>&1

Create config/unicorn-production.rb

You'll need to change [appname] throughout:

# Some config taken from gist: https://gist.github.com/534668

# Our own variable where we deploy this app to
deploy_to = "/home/[appname]/app"

# Use at least one worker per core if you're on a dedicated server,
# more will usually help for _short_ waits on databases/caches.
worker_processes 2

# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
working_directory deploy_to

# Listen on a TCP port
listen 8080

# Restart any workers that haven't responded in 120 seconds (there are some long running requests, eg. reporting)
timeout 120

# feel free to point this anywhere accessible on the filesystem
pid "#{deploy_to}/tmp/[appname].pid"

# By default, the Unicorn logger will write to stderr.
# Additionally, some applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
# stderr_path "#{shared_path}/log/unicorn.stderr.log"
# stdout_path "#{shared_path}/log/unicorn.stdout.log"
#

# combine REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
preload_app false
GC.respond_to?(:copy_on_write_friendly=) and
  GC.copy_on_write_friendly = true

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  # defined?(ActiveRecord::Base) and
  #   ActiveRecord::Base.connection.disconnect!

  # The following is only recommended for memory/DB-constrained
  # installations.  It is not needed if your system can house
  # twice as many worker_processes as you have configured.
  #
  # # This allows a new master process to incrementally
  # # phase out the old master process with SIGTTOU to avoid a
  # # thundering herd (especially in the "preload_app false" case)
  # # when doing a transparent upgrade.  The last worker spawned
  # # will then kill off the old master process with a SIGQUIT.
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

  # # *optionally* throttle the master from forking too quickly by sleeping
  # sleep 1

  #log_env(:before_fork, server)
end


after_fork do |server, worker|
  # worker.user('deployer', 'deployer') if Process.euid == 0

  # per-process listener ports for debugging/admin/migrations
  # addr = "127.0.0.1:#{9293 + worker.nr}"
  # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)

  # the following is *required* for Rails + "preload_app true",
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection

  # if preload_app is true, then you may also want to check and
  # restart any other shared sockets/descriptors such as Memcached,
  # and Redis.  TokyoCabinet file handles are safe to reuse
  # between any number of forked children (assuming your kernel
  # correctly implements pread()/pwrite() system calls)

  # Reconnect memcached
  #Rails.cache.reset
end

# before_exec do |server|
#   paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
#   paths.unshift "#{shared_bundler_gems_path}/bin"
#   ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
#   ENV['GEM_HOME'] = ENV['GEM_PATH'] = shared_bundler_gems_path
#   ENV['BUNDLE_GEMFILE'] = "#{deploy_to}/Gemfile"
# end

Setup recap

recap setup # creates a Capfile

Edit Capfile

  • Update the server address

Bootstrap

bundle exec cap bootstrap

Install bundler on server

sudo gem install bundler

Edit Capfile again

Add this line to the end:

set(:procfile) { "#{deploy_to}/Procfile.production" }

We are currently using this hack – add this to the Capfile:

# TODO: hack.  Foreman now takes a --run option, which recap doesn't have a configuration option for
# https://github.com/tomafro/recap/blob/master/lib/recap/tasks/foreman.rb#L22
set(:foreman_export_command) { "./bin/foreman export #{foreman_export_format} #{foreman_tmp_location} --procfile #{procfile} --app #{application} --user #{application_user} --log #{deploy_to}/log --run #{deploy_to}" }

Commit and push changes

Whoosh.

Install postgres dev headers

sudo apt-get install libpq-dev

Install build tools

sudo apt-get install build-essential

Install Node.js

See here for instructions.

Configure production database

Edit database.yml

production:
  adapter: postgresql
  encoding: unicode
  database: [appname]_production
  pool: 5
  username: [appname]

Then commit and push.

Create postgres user on server

Switch to the postgres user:

sudo su postgres

Create the db:

createdb [appname]_production

Create the user:

psql
create user [appname] with superuser;

Deploy the app

bundle exec cap deploy:setup
bundle exec cap foreman:export
bundle exec cap deploy

Troubleshooting errors

  • You might need to gitkeep some folders to keep sprockets happy

Running unicorn manually

bundle exec unicorn -c /home/[appname]/app/config/unicorn-production.rb -E production

Creating a secret

rake secret

Other unsorted notes

Push as prod env var

cap -vT

Config nginx to proxy any requests to that domain to port 80 to 8080.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment