/ node

Express Site with Digital Ocean and Dokku

Here we're going to look at building a simple site with Node.js, using the Express framework, deployed to Digital Ocean using Dokku.

What's all that?

Chances are, if you're reading this you've at least heard of Node.js and likely Express, however if you're not familiar, I suggest you read up on them, but to summarize:

Node.jsĀ® is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. - http://nodejs.org

Express.js is a Node.js web application framework, designed for building single-page, multi-page, and hybrid web applications. - http://en.wikipedia.org/wiki/Express.js

To install Node.js, follow the instructions on http://nodejs.org/. For Express installation, visit: http://expressjs.com/. From here, I'm assuming you have these installed already.

Digital Ocean is a cloud hosting solution, and Dokku is an application deployment framework. We'll look more at those shortly.

Getting Started

First, we're going to create our appliaction. In this case we'll be building a basic website. We will start with a default site generated by Express. In your terminal window, make sure you're in the directory above where you want your project code, and that the folder for your code does not exist yet. Enter the following command:

express my-site
cd my-site && npm install

What's going on?

Express will generate a typical site sturcture for you, in the folder my-site (or something else if you went that way). The npm install command, installs all the dependanices we need for our application. You can now start the server by typing npm start or node ./bin/www in the project directory. If you point your browser to http://localhost:3000 you should see a basic page served by our application.

"Basic Express Page"

Open folder in your favourite editor. I suggest Sublime Text, but it's really up to you. Take a look at app.js. You can see where it loads the various route files and other middleware. We're not dealing with users right now, so delete the lines:

var users = require('./routes/users');
...
app.use('/users', users);

Also, delete the file routes/users.js. Open routes/index.js. This is the routing for every "top level" page. Anything off the main /. The part we're concerned with is:

router.get('/', function(req, res, next) {
	res.render('index', { title: 'Express' });
});

This looks at the route / (http://localhost:3000/) and then renders the view index.jade and passes that template a varibale called title with the value "Express". For now let's change this to:

router.get('/', function(req, res, next) {
	res.render('index', { title: 'My Site' });
});

Pretty simple - just updating the title. Now take a look at views/index.jade. You should see something like this:

extends layout

block content
	h1= title
	p Welcome to #{title}

Let's edit this just a little, as I find showing the title twice a little redundant. Change this view to:

extends layout

block content
	h1= title
	p This is the home page

Adding More Pages

We can add many more pages, or views to our app. Typical to a website, would be an "About" page. Let's create another view (in the views folder of course) called about.jade, and add this code:

extends layout

block content
	h1= title
	p All about me...

Currently, there is no way to get to this page. We need to create another route. Open up routes/index.js and add:

router.get('/about', function(req, res, next) {
	res.render('about', { title: 'About Me' });
});

Now, after restarting our server, we can goto http://localhost:3000/about and see the new 'about' page we just created.

"About Us Page"

As you can tell, it's a bit of a pain making changes then having to restart the application before being able to see those changes. To deal with this, there are several packages we can use to monitor or app for changes, and restart it as needed. Some are more configuarable than others, and are very suitable for production as well. I've used 3, Supervisor, Forever, and Nodemon. They each have their merits, but for development I prefer Nodemon. So, we're going to use that. Install it globally by typing npm install -g nodemon. With this installed, rather than typing npm start or node ./bin/www to start our app, we will type nodemon ./bin/www. Now when we make a change to our app, the server will restart automatically.

For the purposes of this tutorial, we don't really need to add another page, however if you were adding something else you would do it the same way we did for /about. The next production site I need to build, is for a client that will have a "For Sale" page. So, I'll make route for that (who knows, this code might get used for that site in the end). Let's add view/for-sale.jade and put:

extends layout

block content
	h1= title
	p Check out these items available on Etsy!

Then we can add a route:

router.get('/for-sale', function(req, res, next) {
	res.render('for-sale', { title: 'For Sale' });
});

If we were now adding many pages in this fashion, routes/index.js could get really long, really fast! So, maybe not an ideal long term solution, but for what I'm building now, and the purposes of this tutorial, it's fine. We can point our browser to http://localhost:3000/for-sale and see our new page. Notice we did not restart the server!

"For Sale Page"

Time to Deploy

OK, we're going to actually need to slow down just a little. Yes, we will deploy shortly. No it's not that hard. Yes, you need to do some work.

First, get setup on Digital Ocean

What is Digital Ocean?

As I mentioned above, Digital Ocean is a cloud hosting solution, created for developers. This means that server configuration is primarily your responsibility, however they do have a number of applications already configured for installation. All servers run on solid state drives for speed and reliability, the servers and network are incredibly fast, and they offer private networking to allow your "Droplets" to talk to each other. By the way, a "Droplet" is a server in the Digital Ocean environment.

Create your account (using this link gets you a $10 credit, good for 2 months hosting of the Droplet we're using). Then create a new Droplet by clicking the "Create Droplet" button. For the purposes of this tutorial, I am using my-site.markrabey.com as the URL, and therefore the name of my Droplet. The $5 per month server, is more than enough for what we're doing right now, but obviously if you're site is getting more traffic, or needs the storage space, you can upgrade. You can increase the size of your Droplet later with only about a minute of downtime.

"Digial Ocean Setup"

Select any region you would like, I happened to select "New York 3", only because it's selected by default.

"Digial Ocean Setup"

You can leave "Available Settings" blank (defaults). Then, under "Select Image", click the "Applications" tab and select "Dokku v0.3.13 on 14.04 (w/ Docker 1.4.1)" - or whichever version happens to be available to you.

"Digial Ocean Setup"

Adding an SSH key means you won't need to enter a password to push code to your server, or to SSH into it. It means a root password will not be emailed to you, and is generally more secure than simple password authentication. You don't need to add one if you don't want to, but it's highly recommended. To do this, click "Add SSH Key". To generate a public key, type the following into your command prompt:

ssh-keygen -t rsa

Follow the prompts to generate a key. Entering a pass phrase is optional, but does add a level of security. Copy the key to your clipboard by typing:

pbcopy < ~/.ssh/id_rsa.pub

Paste that into the box on Digital Ocean, and name the key whatever you would like.

Now, click "Create Droplet". It takes about a minute to create, then it's almost ready to use. Dokku works best with a domain or sub-domain, as opposed to using an IP address. So, you need to add an A record to your DNS settings. I happen to use Digital Ocean for my DNS as well, but that doesn't really matter. Make note of the IP address assigned to your Droplet, and add the A record. In my case, I already have markrabey.com setup, I just added a record to point my-site to my new IP.

"Add DNS Record"

Now, I can reference my site at: http://my-site.markrabey.com. Going there now, I get this screen:

"Dokku Setup"

On this screen, edit the "Hostname" field to replace the IP. Again, in my case it's my-site.markrabey.com. Now, check off "Use virtualhost naming for apps". This allows us to build multiple apps off the same hostname. We're not doing that now, but if you were to add a second app to the same server it would be nicer to have it as a subdomain, rather than using a port number. Though that's really up to you, and not being covered here at all. Click "Finish Setup", you are now directed to the Dokku Documentation, well worth reading, in fact, the next few steps are basically covered on that page. Now, if I go to http://my-site.markrabey.com/ (or whatever you're using) we actually get an error. That's because there is no code on our server yet, so let's do that! But first...

What is Dokku?

Dokku is "mini-Heroku" built on Docker for deploying and distributing Node.js applications. Ok...that's not helpful I suppose. First, Docker is a platform for managing distributed applications. This includes building, shipping, and running these applications. Heroku is also a platform for building and deploying applications. Although similar in concept, they are quite different. Rather than cover the differences here, take a look at Docker vs. Heroku by Thomas Uhrig. He covers both very well.

Dokku makes use of the best parts of each. It allows us to deploy by pushing to a Git repo, and using Buildpacks like Heroku does, but allows us to manage our apps in Docker containers. I honestly haven't used either on there own, and given the ease of use with Dokku, likely won't. Read the full documentation on Dokku here.

Now we can deploy

Deploying to Dokku is really simple. We just need to git push to a particular remote. First, we need to make our project a Git repository. In your terminal, type:

git init

This creates a repository in our project directory. Next, create a new file called .gitignore and add:

node_modules

This makes sure we don't copy those with our code unnecessarily, as Dokku will install any dependencies that are needed. Now, back in your terminal, type the following commands:

git add .
git commit -m "initial commit"
git remote add dokku dokku@my-site.markrabey.com:my-site
git push dokku master

The first line adds the files to be committed. Then we commit them with a simple message. Next we add a remote called dokku at the location dokku@my-site.markrabey.com:my-site. In this case, dokku@ refers to the user dokku on our server, in my case my-site.markrabey.com. The :my-site is the name of our app. Remember when I mentioned multiple apps on the same server, that's where the app name is important. I'll cover this in an upcoming tutorial. The last command pushes our code to our Droplet. Wait a minute or so, read the output, then we can hit our URL and get our site!

"Hosted!"


Feel free to play around with content, and templates. For more reading, checkout the following:

And please, check back for some future tutorials I want to write as a result of this one:
(in no particular order)

  • Using multiple Accounts with different SSH keys
  • Building templates with Jade
  • Hosting multiple apps on one Digital Ocean server

Please feel free to comment and share!