Skip to content

Instantly share code, notes, and snippets.

@heycarsten
Last active October 7, 2015 23:08
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save heycarsten/3239720 to your computer and use it in GitHub Desktop.
Save heycarsten/3239720 to your computer and use it in GitHub Desktop.
Dumb-Dumb's Guide To Manually Provisioning A Linode For A Ruby App

Manually Provisioning A Linode For A Ruby App

Alright, let's get started. Create a new Ubuntu 12.04 LTS instance, boot it up, and SSH into the fresh Linode as root. Now update/upgrade your sources and install Git and curl:

apt-get update
apt-get upgrade
apt-get install git-core curl

After this, you'll probably have to reboot the machine.

Now set the hostname, I often like to use nameofapp-main as the initial/sole instance.

echo "myapp-main" > /etc/hostname
hostname -F /etc/hostname

Now assign your machine a FQDN (Fully Qualified Domain Name)

vim /etc/hosts

Somewhere, usually at the top just blow the localhost.localdomain bit, substitute 0.0.0.0 with the public IP address of the machine.

0.0.0.0 myhostname.myapp.com myhostname

Set the system locale to UTF-8, so Postgres doesn't build DBs in ASCII.

locale-gen en_US.UTF-8
update-locale LANG=en_US.UTF-8

Configure the timezone, I recommend Etc/UTC because it's for real.

dpkg-reconfigure tzdata

Now, add your (and whoever else's) public key(s) to the root account:

mkdir ~/.ssh
cd ~/.ssh
vim authorized_keys

Set the correct permissions:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Log out of the machine and try logging in again, if it works without a password then it's time to disable noob-ass password auth:

vim /etc/ssh/sshd_config

Now, modify or add PasswordAuthentication no and...

service ssh restart

Create the deploy user:

useradd -m -s /bin/bash deploy

Copy over SSH access stuff to the deploy user, you might want this to be different than the access for root, in that case, do your thing.

mkdir /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh/

Then install Postgres 9.2:

First, I update the shit according to this article:

http://michael.otacoo.com/postgresql-2/take-care-of-kernel-memory-limitation-for-postgresql-shared-buffers/

Then:

sudo vim /etc/apt/sources.list.d/pgdg.list

And add the following line to that file:

deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main

If you're not using precise, then run lsb_release -c to get the codename of the release you are running and substitute accordingly.

Next add the Postgres repository key:

wget --quiet -O - http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add -

Now we can install Postgres:

sudo apt-get update
sudo apt-get install postgresql-9.2 libpq-dev postgresql-contrib-9.2

After that I run pg_tune:

cd ~/src
wget http://pgfoundry.org/frs/download.php/2449/pgtune-0.9.3.tar.gz
tar xzf pgtune-0.9.3.tar.gz
sudo ./pgtune-0.9.3/pgtune -i /etc/postgresql/9.2/main/postgresql.conf -o /etc/postgresql/9.2/main/postgresql.conf.pgtune
sudo mv /etc/postgresql/9.2/main/postgresql.conf /etc/postgresql/9.2/main/postgresql.conf.original
sudo mv /etc/postgresql/9.2/main/postgresql.conf.pgtune /etc/postgresql/9.2/main/postgresql.conf
sudo chown postgres:postgres /etc/postgresql/9.2/main/postgresql.conf

Create a "deploy" user that can't create databases or roles, and then create your app's database(s) under that user:

sudo su - postgres
createuser -D -R deploy
createdb -O deploy myapp_production

Note: By default Postgres will not bind to any IP interfaces, given the context of this guide, that is a perfectly acceptable default.

Time to install Redis for all your NoSQL needs. Create a src directory in root's home directory:

cd ~
mkdir src
cd src

Now go to the Redis website and wget the latest stable version.

wget {{redis-stable-tgz-url}}
tar -xzf {{redis-stable-tgz-file}}
cd {{uncompressed-redis-dir}}
make 32bit   # Obvs remove the 32bit part if this is a 64bit instance.
make install

Now time to configure Redis, copy the redis.conf file from the Redis source directory into /etc and go to town:

cp redis.conf /etc/redis.conf
useradd -r redis
mkdir -p /var/log/redis          # <- Unless you use syslog instead
chown redis:redis /var/log/redis # <- Unless you use syslog instead
mkdir /var/lib/redis
chown redis:redis /var/lib/redis

Modify redis.conf (the Redis config file) to point to above directories, now is also a good time to ensure that you are only binding to 127.0.0.1:

bind 127.0.0.1 # <- Make sure this line is in /etc/redis.conf

In addition, since we're going to use Upstart to manage the Redis service now is a good time to ensure that the following is set in your redis.conf:

daemonize no

I urge you to read through your Redis config file and tailor it to your needs, it's documented very well.

Now create an Upstart script to manage the Redis service. Create a file at /etc/init/redis.conf and add the following to it:

description "Redis"
start on runlevel [23]
stop on shutdown
exec sudo -u redis /usr/local/bin/redis-server /etc/redis.conf
respawn

Finally, start Redis:

start redis

Time to compile and install Nginx, download the latest stable source from the Nginx Wiki, then download the latest version of the Nginx Upload Module, then compile and install:

Note: At the time of this writing, there is a bug with the downloadable version of the Nignx Upload Module 2.2.0 and the version of GCC on Ubuntu 12.04, to work around this issue, check-out this specific version of the Nignx Upload Module into your /src directory and compile against it instead of the downloadable version above:

git clone -b 2.2 git://github.com/vkholodkov/nginx-upload-module.git nginx_upload_module-2.2.0
apt-get install libpcre3-dev
./configure --with-http_ssl_module --with-http_gzip_static_module --add-module=../nginx_upload_module-2.2.0
make
make install

Then create an Nginx user and a log directory:

useradd -r nginx
mkdir /var/log/nginx
chown nginx:nginx /var/log/nginx

Now copy over your favorite Nginx config into /etc/nginx.conf:

cp nginx.conf /etc/nginx.conf

Create an Upstart process for Nginx, insert the following into /etc/init/nginx.conf:

# nginx
 
description "Nginx HTTP Daemon"
author "George Shammas <georgyo@gmail.com>"
 
start on (filesystem and net-device-up IFACE=lo)
stop on runlevel [!2345]
 
env DAEMON="/usr/local/nginx/sbin/nginx -c /etc/nginx.conf"
env PID="/var/run/nginx.pid"

expect fork
respawn
respawn limit 10 5
 
pre-start script
        $DAEMON -t
        if [ $? -ne 0 ]
                then exit $?
        fi
end script
 
exec $DAEMON

Now it's time to install Ruby, use RVM, you will likely have to temporarily add the deploy user to sudoers in order to let RVM do it's automagic library install OR you can disable that probably and install them via your admin user.

Then install a Ruby:

rvm install 2.0.0

Make it the default interpreter for the deploy user:

rvm --default use 2.0.0

The base system is now provisioned! You have most of a full stack which you can deploy Ruby apps on with a database and Redis for running Sidekiq workers.

I have not covered a few things, for example logfile rotation. I feel that this is sort of a service-level decision, there are some different options or maybe you're using syslog, I leave that up to you.

I have not covered setting up a mail server such as Postfix, Linode provides some bad-ass guides if you choose that route. You might opt to use a third-party service such as Amazon's SES, or Sendgrid, I have used both and they are good options. Do some research and choose the best option for your needs.

I have not covered deploying a Ruby app to your new server either, I feel like that's outside the scope of this guide, but whatever you do deploy with Capistrano, and use Unicorn, or Puma, it looks pretty neat these days and I've used it successfully in production.

♥ ♥ ♥

@elucid
Copy link

elucid commented Aug 8, 2012

you forgot apt-get install emacs23-nox

@heycarsten
Copy link
Author

@elucid Oh, you'd like that, wouldn't you!

@elucid
Copy link

elucid commented Aug 12, 2012

Pretty important: set up a private IP address, even if you don't need it right away. If you don't have a private IP address, you won't be able to e.g. connect to a database node in the same data centre, use NodeBalancer, etc. Also, adding a new interface requires a reboot of the machine so it should be done at setup time. See: http://library.linode.com/networking/configuring-static-ip-interfaces

@elucid
Copy link

elucid commented Aug 12, 2012

Also, there should probably be some mention of firewalls as well. Even if it's just

$ ufw allow 22
$ ufw enable

followed by a link to https://help.ubuntu.com/12.04/serverguide/firewall.html

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