In this post I’ll explain how to easily publish a simple static website using npm, Grunt and Amazon S3. I assume you already have an AWS account in place. I wrote this guide using OS X Yosemite, so you will need to adapt it to other systems.

First, what is Node.js?

Node.js is a platform for building JavaScript applications. It is based on the runtime shipped with the Google Chrome web browser. My favourite way to install it is using Homebrew:

$ brew install node

Brilliant, now let’s ensure Grunt is installed globally using npm:

$ npm install -g grunt-cli

Grunt is a JavaScript task runner. It is similar to Phing in PHP or Rake in Ruby. Now we can create an empty directory to hold our project:

$ mkdir ~/Sites/mystaticwebsite
$ cd ~/Sites/mystaticwebsite

And create a public directory to store our HTML:

$ mkdir public

Managing the dependencies

We’ll use npm to create a new package.json file in the root of our project. npm uses this file to manage our project’s dependencies.

$ npm init

I normally push enter a few times and accept the defaults. My package.json file now looks like this:

  "name": "simplestaticsite",
  "version": "1.0.0",
  "description": "simple static site",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "grunt": "^0.4.5"

We also need to add a few other libraries we’ll use later:

$ npm install grunt-aws --save-dev
$ npm install grunt-contrib-connect --save-dev

Ok great, in the next step let’s create a Gruntfile.

Creating the Gruntfile

Grunt uses a file called Gruntfile.js to manage common tasks associated with your project. Create this file and paste in the following code:

module.exports = function(grunt) {

  // Load S3 plugin

  // Static Webserver

  // Project configuration.
    pkg: grunt.file.readJSON('package.json'),
    aws: grunt.file.readJSON('.aws.json'),
    s3: {
      options: {
        accessKeyId: "<%= aws.accessKeyId %>",
        secretAccessKey: "<%= aws.secretAccessKey %>",
        bucket: "<%= aws.bucket %>"
      build: {
        cwd: "public",
        src: "**"
    connect: {
      server: {
        options: {
          port: 8000,
          base: "public",
          keepalive: true

  // Default task(s).
  grunt.registerTask("default", ["connect"]);


In my Gruntfile I load the required plugins and configure two tasks. The first task is responsible for publishing to S3. The second task sets up a basic webserver we can use to preview our site when we are developing it. I have marked the webserver as the default task. We also need to make a file called .aws.json to store our AWS credentials. A word of warning here. Never commit your AWS credentials if you are using Github to version your code. I’ve heard plently of stories of bots automatically extracting them and wreaking some havoc within your account. My .aws.json file looks like this (with the AWS credentials redacted):

  "bucket": "rob-test-bucket777",
  "accessKeyId": "ACCESS_KEY_ID",
  "secretAccessKey": "SECRET_ACCESS_KEY"

Be sure to insert your access keys and bucket name or you will not be able to publish to S3.

Creating some HTML

Before we publish our site we actually need to make one. Create an empty HTML file:

$ touch public/index.html

Open it in your text editor and add some basic text, e.g:

my site works!

Lets take a quick look at it, try running:

$ grunt

Then open the following URL in your browser:

Great now we can publish to S3!

Publishing to S3

AWS is a great platform and S3 has a feature for serving static websites. We’ll use the Grunt grunt-aws task we installed earlier to easily publish our site to S3. Run the command:

$ grunt s3

If you correctly configured your AWS credentials and bucket name, Grunt will connect to S3 and publish your website!

Configuring S3 to serve your website

Finally we need to ensure S3 is configured to serve our site using a bucket or custom domain name. For more information on how to configure a static site using an S3 bucket, follow the article here:

Wrap Up

We have now published a simple static site using npm, Grunt and S3. If you have a rich static site you could look at integrating CSS & JavaScript precompilers for SASS or CoffeeScript. You could also investigate other static hosting solutions such as Github pages or Divshot. If you have any questions send me an email or tweet.

Rob Morgan

CTO based in Berlin, Germany. Creator of #Phinx, Australian, Startups, Technology, Travel.

robmorgan _rjm_