Overview
This guide will walk you through configuring and deploying your Spree application to an environment on Ubuntu 13.04.
This guide assumes an absolutely clean-slate Ubuntu 13.04 Server install, and covers setting up the following things:
- A user for the application
- Operating system dependencies required for Ruby, Rails and Spree
- Ruby 2.1.0
- Rails 4.0.2
- PostgreSQL
- Unicorn + nginx (with SSL)
- Seed data for your store
Initial Server Setup
The first thing you will need on your server is a user account on the server which will be responsible for providing a container for your application’s install.
A user account
For the purposes of this guide, the user’s account on the system will be called “spree”, but you may choose to call it whatever you wish.
To set up this new user, run these commands on the server:
$ useradd -d /home/spree -m -s /bin/bash spree $ passwd spree
Set a new password for the user and remember it, as you will require it in just a moment.
Key-based authentication
The next thing to set up is secure key-based authentication on the server. This will involve setting up a private key on your system, copying over the related public key to the server, asserting that you can now login without providing a password, and then disabling password authentication on the server to increase security.
On the remote server, set up an .ssh
directory to contain the new public key
for a user by running these commands:
$ mkdir /home/spree/.ssh $ chown spree:spree /home/spree/.ssh $ chmod 700 /home/spree/.ssh
This directory is used to authenticate key-based authentication when using SSH.
On your local machine, generate a private key using ssh-keygen
like this:
$ ssh-keygen -t rsa
Set the filename to be [your home directory]/.ssh/spree_rsa.
You can choose to enter a password if you wish. All that would mean is that you would need to provide that password to use the key.
If you already have a private key, you can use that one.
Once you’ve finished generating this key, you will need to copy the public version of this key over to the new server. To do this, run this command:
$ scp ~/.ssh/spree_rsa.pub spree@[your server's address]:~/.ssh/authorized_keys
The password you will need to enter here is the password for the user account on the remote server.
Once you’ve set this up, you will then be able to use key-based authentication to connect to the server:
$ ssh spree@[your server's address] -i [your home directory]/.ssh/spree_rsa
To save having to use the -i
option here, you can place the following lines
inside .ssh/config
on your local machine:
Host [your server's address]
IdentityFile ~/.ssh/spree_rsa
You should now follow the same steps for the root
user on your server as well,
so that you can authenticate with the same key to access both the deployment
user and root accounts. You may choose to use a completely different key if you
wish.
Once you have verified – by connecting via SSH to the remote server – that both accounts work without password authentication, you can now disable password-based authentication.
To disable password-based authentication, you will need to uncomment this line
within /etc/ssh/ssh_config
and change the “yes” value to “no”:
#PasswordAuthentication yes
It should be this when you’re done:
PasswordAuthentication no
Then you will need to restart the SSH daemon on the server, by running this command:
$ service ssh restart
After this, if you attempt to run ssh spree@localhost
from within the server
itself, it will return “Permission denied (publickey)”, indicating that it has
not attempted to authenticate with a password, but instead with a publickey,
which the server does not have configured.
Now that the user is set up on your system and access to it and root’s account are locked down a bit tighter, it’s time to set up Ruby.
Operating System Dependencies and Ruby
To install Ruby, you are going to use the “RVM”:http://rvm.io tool. This tool provides a simple way of installing a version of Ruby onto your server.
To install it, run these commands:
$ curl -L https://get.rvm.io | bash -s stable $ . ~/.bashrc
Next, you will need to install the operating system dependencies required for Ruby. Run this command to install the dependencies:
rvm requirements
You will also need to install a JavaScript runtime. You can install the nodejs
package from apt-get
:
$ apt-get install -y nodejs
Or, you can put a dependency for therubyracer
gem into your Gemfile
:
group :production do gem 'therubyracer' end
You will also need the imagemagick
package, which is used to handle image
manipulation which is used when you upload product images in your store:
$ apt-get install -y imagemagick
Once these dependencies are installed, switch back into the spree
user and
install Ruby 2.1.0 by running this command:
$ rvm install 2.1.0
This command will take a couple of minutes to finish running.
Once it’s finished running, run this command to make that version of Ruby the default for this user:
$ rvm use 2.1.0 --default
Ensure that this version of Ruby is really the new default by running this command:
ruby -v
It should output something similar to this:
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]
You now have a version of Ruby correctly configured on your server.
Deploying to the server
The next step is to put your Spree application onto the server. To do this, you will use the deployment tool called Capistrano.
Install Capistrano on your local system by running this command:
$ gem install capistrano
Then, inside the directory for your Spree app, run this command to set up a Capistrano deploy configuration:
$ capify .
This command will create two files: a Capfile
and a config/deploy.rb
. The
config/deploy.rb
file is where you will be configuring how Capistrano chooses
to deploy your application. Open this file now and you will see the following
lines (comments removed):
set :application, "set your application name here" set :repository, "set your repository location here" set :scm, :subversion # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` role :web, "your web-server here" # Your HTTP server, Apache/etc role :app, "your app-server here" # This may be the same as your `Web` server role :db, "your primary db-server here", :primary => true # This is where Rails migrations will run role :db, "your slave db-server here"
The contents of this file tell Capistrano about the deployment of your application.
The application
variable tells Capistrano the name of your application, and
the repository
variable tells it where it can find the source of your
application. The scm
variable tells Capistrano the type of source control
system you’re using. If you’re using Spree, there’s a high chance that’s going
to be :git
, so change that over now. Change the application
and repository
now to accurately reflect your application.
The next batch of things to configure in this file are the different “roles”. These tell Capistrano which servers play which roles within your server architecture. Within this guide, you’ve been working with a single server and will continue to do so. Therefore, these roles should look like this:
server = "[your server's address]" role :web, server role :app, server role :db, server, :primary => true
After this, you will need to tell Capistrano the account name to use for
deploying to your server. In this guide, we’ve used “spree” so far, but you may
have chosen to use something different. To tell Capistrano the user to use, put
this line inside your config/deploy.rb
:
set :user, "spree"
You will also need to tell it the path to deploy at. By default in Capistrano,
this path is /u/apps/[application_name]
. There is probably no /u/
directory on the
server, so that won’t work for you immediately. You already have a
self-contained user account on the server, so deploying the application to that
user’s home directory would make better sense. Add this line to
config/deploy.rb
to do that:
set :deploy_to, "/home/spree/#{application}"
You will also need to tell Capistrano to never use sudo, since you’re going to be operating as a user without sudo permission:
set :use_sudo, false
Along with this, you will also need to tell it to use the bash
shell, as you
will need access to the commands for gems such as bundler
, which are provided
by RVM.
default_run_options[:shell] = '/bin/bash --login'
And because all the Rails-specific commands are going to need to run on the production environment, it’d be a great idea to add this to the configuration as well:
default_environment["RAILS_ENV"] = 'production'
With that configuration, your config/deploy.rb
should look like this:
set :application, "[name]" set :repository, "[repository]" set :scm, :git server = "[your server's address]" role :web, server role :app, server role :db, server, :primary => true # This is where Rails migrations will run set :user, "spree" set :deploy_to, "/home/spree/#{application}" set :use_sudo, false default_run_options[:shell] = '/bin/bash --login' default_environment["RAILS_ENV"] = 'production'
To set up the server for Capistrano, run cap deploy:setup
. This will create
the required Capistrano directories for your application inside
/home/spree/[name]
.
You will need to add another two lines to the top of this file as well so that the application’s gem dependencies are installed onto the server with Bundler, and the assets are precompiled. These two lines are this:
require "bundler/capistrano" load "deploy/assets"
To attempt to deploy the actual application to the server, run cap deploy
. If
the repository
option points to GitHub, this will fail because the server has
never verified GitHub’s host key.
[[your server's address]] executing command
** [[your server's address] :: err] Host key verification failed.
** [[your server's address] :: err] fatal: The remote end hung up unexpectedly
To verify this, run this command:
$ ssh github.com
This will ask you to verify that GitHub’s RSA key fingerprint is
16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
. If that’s correct, type “yes”
to the prompt and GitHub’s host key will now be verified.
When you run cap deploy
again, you’ll see this error:
[[your server's address]] executing command
** [[your server's address] :: err] Permission denied (publickey).
** [[your server's address] :: err] fatal: The remote end hung up unexpectedly
This means that your application does not have a deploy key setup for it on GitHub. To set up a deploy key for the application, run this command as the “spree” user on the server:
$ ssh-keygen -t rsa
Do not enter a password for this key. Otherwise you will need to use it every time you deploy.
Run cat ~/.ssh/id_rsa.pub
on the server to get the deploy key. Go to your
application’s repository on GitHub and go to “Settings”, then “Deploy Keys” and
enter the whole key into the form there, giving it a memorable name if you ever
need it again.
When you run cap deploy
again, Capistrano will clone your application’s code
from GitHub to your server into a directory such as /home/spree/[application's
name]/releases/[timestamp]
. Inside this directory – because you’re requiring
the bundler/capistrano
tasks at the top of the config/deploy.rb
file –
Capistrano will run bundle install
with a couple of options, pointing Bundler
at the application’s gemfile and placing the gems into a shared directory at
/home/spree/[application]/shared/bundle
. This is so that every release of your
application can use the same bundle. This bundle install
command will also not
install the development and test dependencies for your application, as you won’t
need them on your production server.
The cap deploy
command this time will also run an asset precompilation step,
thanks to the deploy/assets
loading at the top of config/deploy.rb
. This
step will compile the assets to the public/assets
directory in the current
release’s directory.
The next step is to set up a database server for the data for your Rails app.
Setting up a database server
The database server we will be using in this guide will be the “PostgreSQL”:http://postgresql.org database server. Once this is setup, you will be able to tell capistrano to run the migrations on your application, creating the necessary tables for your Spree store.
To install PostgreSQL, run this command:
$ apt-get install -y postgresql
You will also need to install its development headers, which the pg
gem will
use to connect to the database:
$ apt-get install -y libpq-dev
Once those two packages are installed, you will need to create a new database
for your application to use. This database should have the same name as the
server’s deploy user account, which in this guide has been “spree” so far. Yours
could be different. To set up this database, run this command as root
:
$ sudo -u postgres createdb spree $ sudo -u postgres createuser spree
To get your application to connect to this database, you will need to set up a
database.yml
file on the server. This file needs to be kept on the server in a
common location where it can be copied over into the latest deployed version of
the application, and so you should place it at /home/spree/[application's
name]/shared/config/database.yml
. Inside this file, put this content:
production: adapter: postgresql database: spree
If you’re not already using the PostgreSQL adapter on your application, as
specified by gem 'pg'
in your Gemfile
, you’ll need to add this gem to your
Gemfile
now, inside a production
group:
group :production do gem 'pg' end
If you need to add this gem, you will need to run bundle install
on your
server and commitpush your
Gemfile and
Gemfile.lock` to Git.
You should always develop and deploy on the same database adapter! If you don’t, you may run into incompatibility issues between your development and deployment setups which can be difficult to track down.
Now when you run cap deploy
, you will need Capistrano to automatically copy
over the database.yml
file from the shared directory into the current deploy
path. To make Capistrano do this, put these lines into config/deploy.rb
for
your application:
task :symlink_database_yml do run "rm #{release_path}/config/database.yml" run "ln -sfn #{shared_path}/config/database.yml #{release_path}/config/database.yml" end after "bundle:install", "symlink_database_yml"
After bundle install
has finished running on the server, Capistrano will now
copy over the config/database.yml
into the current path. In order for
Capistrano to deploy and run your migrations, you will need to ru
cap
deploy and then
cap deploy:migrate`. Run those commands now. You should see
the migrations run on the server.
The next step is to set up the web server to serve requests for your application.
Setting up a web server
The web server you’ll be using here will be the Unicorn web server which will run the Rails application instances, and then those instances will have an nginx frontend which will serve the requests coming from the people who are visiting your store.
Setting up Unicorn
To set up unicorn for your application, add the unicorn
gem to your Gemfile
,
inside the production
group:
group :production do gem 'pg' gem 'unicorn' end
Unicorn requires some configuration in order to work, which belongs in
config/unicorn.rb
. This is the content required for Unicorn:
# config/unicorn.rb # Set environment to development unless something else is specified env = ENV["RAILS_ENV"] || "development" # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete documentation. worker_processes 4 # listen on both a Unix domain socket and a TCP port, # we use a shorter backlog for quicker failover when busy listen "/tmp/[application's name].socket", backlog: 64 # Preload our app for more speed preload_app true # nuke workers after 30 seconds instead of 60 seconds (the default) timeout 30 pid "/tmp/unicorn.[application's name].pid" # Production specific settings if env == "production" # Help ensure your application will always spawn in the symlinked # "current" directory that Capistrano sets up. working_directory "/home/spree/[application's name]/current" # feel free to point this anywhere accessible on the filesystem user 'spree' shared_path = "/home/spree/[application's name]/shared" stderr_path "#{shared_path}/log/unicorn.stderr.log" stdout_path "#{shared_path}/log/unicorn.stdout.log" end 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 if defined?(ActiveRecord::Base) ActiveRecord::Base.connection.disconnect! end # Before forking, kill the master process that belongs to the .oldbin PID. # This enables 0 downtime deploys. old_pid = "/tmp/unicorn.[application's name].pid.oldbin" if File.exists?(old_pid) && server.pid != old_pid begin Process.kill("QUIT", File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH # someone else did our job for us end end end after_fork do |server, worker| # the following is *required* for Rails + "preload_app true" if defined?(ActiveRecord::Base) ActiveRecord::Base.establish_connection end # 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) end
Remember to replace [application's name]
above with your actual application
name. Run bundle install
and commit and push your Gemfile
, Gemfile.lock
and config/unicorn.rb
files to Git.
Next, you will need to add tasks to Capistrano to ensure that the Unicorn
workers are restarted after a cap deploy
. To do this, put these lines into
your config/deploy.rb
:
namespace :unicorn do desc "Zero-downtime restart of Unicorn" task :restart, except: { no_release: true } do run "kill -s USR2 `cat /tmp/unicorn.[application's name].pid`" end desc "Start unicorn" task :start, except: { no_release: true } do run "cd #{current_path} ; bundle exec unicorn_rails -c config/unicorn.rb -D" end desc "Stop unicorn" task :stop, except: { no_release: true } do run "kill -s QUIT `cat /tmp/unicorn.[application's name].pid`" end end after "deploy:restart", "unicorn:restart"
Commit your config/deploy.rb
to Git, push the changes to GitHub and run cap
deploy
again to ensure the latest code is available on your server. This will
include the unicorn
gem which will be vital for the next step: setting up
nginx and getting it to serve requests from your application.
Setting up nginx
To install nginx, run this command as root
:
$ apt-get install nginx
Once this command is installed, you will then need to configure nginx to serve
requests from your unicorn workers. To do this, put this content inside
/etc/nginx/nginx.conf
:
user spree;
# Change this depending on your hardware
worker_processes 4;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
}
http {
types_hash_bucket_size 512;
types_hash_max_size 2048;
sendfile on;
tcp_nopush on;
tcp_nodelay off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
gzip_proxied any;
gzip_min_length 500;
gzip_types text/plain text/css application/json application/x-javascript
text/xml application/xml application/xml+rss text/javascript;
## # Virtual Host Configs ##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
This file sets up nginx-specific settings. You will need another file to tell
nginx where your application is. Create another file at
/etc/nginx/sites-enabled/[your application's name]
, and fill it with this
content:
upstream [your server's address] {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
server unix:/tmp/[your application's name].socket fail_timeout=0;
}
server {
# if you're running multiple servers, instead of "default" you should
# put your main domain name here
listen 80 default;
# you could put a list of other domain names this application answers
server_name [your server's address];
root /home/spree/[your application's name]/current/public;
access_log /var/log/nginx/[your server's address]_access.log;
rewrite_log on;
location / {
#all requests are sent to the UNIX socket
proxy_pass http://[your server's address];
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
# if the request is for a static resource, nginx should serve it directly
# and add a far future expires header to it, making the browser
# cache the resource and navigate faster over the website
location ~ ^/(system|assets|spree)/ {
root /home/spree/[application's name]/current/public;
expires max;
break;
}
}
The final location
block here tells nginx to serve asset requests for three
separate paths from the public
directory: system
, assets
and spree
.
The system
directory is where Paperclip typically would store assets. Spree’s
assets are located separately, under spree
.
The assets
directory will contain the other assets for your application from
the asset pipeline.
With these settings in place, you can start up nginx by running service nginx
start
as root on the remote server. Next, you can start the unicorn processes
by running cap unicorn:start
from your local machine. Once these are running,
you will be able to access your site at [your server’s address]. You should see
your store’s homepage here if everything is correctly set up.
Setting up SSL
This part of the guide assumes you have the relevant SSL certificate files
(a file ending in .crt
, and another in .key
) already and just need to know
where to put them.
The *.crt
file belongs in /etc/ssl/certs
, and the *.key
file belongs in
/etc/ssl/private
. Put these files there now.
To get nginx to work with SSL, you will need to edit
/etc/nginx/sites-enabled/[application's name]
and inside the server {
block,
put these lines:
listen 443 ssl;
ssl_certificate /etc/ssl/certs/[your certificate's name].crt;
ssl_certificate_key /etc/ssl/private/[your key's name].key;
Take this time to ensure that you definitely have this line inside this file as well:
proxy_set_header X-Forwarded-Proto $scheme;
Without this line, you would get a redirect loop when you attempted to sign in to your Spree store.
That is all the SSL configuration you will need for your server. To verify that it works, attempt to visit the login page for your application, or the admin area.
Loading seed data
Now with the database and web servers set up for your Spree store correctly, the final thing you will need is seed data. This data contains things such as countries, states, zones, zone members and an admin role.
To install this data, run this command on the server, inside the current directory:
RAILS_ENV=production bundle exec rake db:seed
If you have spree_auth_devise
installed, this command will also prompt you for
a username and password for your admin user. If you’re not using
spree_auth_devise
, then you will need to set up a new user account manually in
the console and assign it the admin role, like this:
user = User.create!(:email => "[email protected]", :password => "topsekret") user.spree_roles << Spree::Role.find_by_name("admin") user.save!
Note that your User
model may require additional attributes before it can be
created.
Once you have the data seeded by the rake db:seed
command, you will need to
log into the admin interface and create a shipping method and a payment method
so that orders can be delivered and paid for.
Symlinking images
You don’t need to follow the steps in this section if you’re using S3 or another cloud hosting provider. This section is only necessary for local file storage.
The final step in configuring the server is symlinking the images so that on
subsequent deploys they don’t disappear. To do this, you can create a new spree
directory within the shared
directory by using this command:
mkdir -p /home/spree/[application's name]/shared/spree
This is the directory where all the uploads for the application will live. This
directory should be symlinked over to the application upon every deployment, and
to do that you can add this content to your config/deploy.rb
:
namespace :images do task :symlink, :except => { :no_release => true } do run "rm -rf #{release_path}/public/spree" run "ln -nfs #{shared_path}/spree #{release_path}/public/spree" end end after "bundle:install", "images:symlink"
Now upon every deploy, Capistrano will symlink the spree
directory in the shared
directory into the current version of the app so that the product images are
persisted across releases.