Node.js Koa Container

An example of how to create a Docker container application using Koa.js Next generation web framework for Node.js.

In the project root, initialize using Yarn or npm.

yarn init -y

Install dependencies.

yarn add koa
yarn add koa-body
yarn add koa-logger
yarn add koa-router
yarn add koa-views
yarn add swig

Create an app folder in the project root.

In the app folder, create a folder named lib. Then create this render.js module in the new lib folder.

 * Module dependencies.

const views = require('koa-views');
const path = require('path');

// setup views mapping .html
// to the swig template engine

module.exports = views(path.join(__dirname, '/../views'), {
  map: { html: 'swig' }

In the app folder, create a folder for templates named views. Then create this index.html template in the new views folder.

<!DOCTYPE html>
<html lang="en">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  • Using Emmet, which is built into VS Code, you can create the index.html content by entering an exclamation mark on the first line then select the tab key.

In the app folder, create this app.js application entrypoint file.

const render = require('./lib/render');
const logger = require('koa-logger');
const router = require('koa-router')();
const koaBody = require('koa-body');

const Koa = require('koa');
const app = module.exports = new Koa();

// middleware




// route definitions

router.get('/', index);


async function index(ctx) {
  await ctx.render('index', { content: 'Hello World' });

// listen

if (!module.parent) app.listen(3000);

Project structure

  • project
    • package.json
    • app
      • app.js
      • lib
        • render.js
      • views
        • index.html

Test the application locally in a browser at http://localhost:3000. Use Ctrl + C to kill the app after verifying it works.

cd app
node app.js


To containerize the application, create a docker-compose.yml file in the project root as follows.

version: '3'

    image: node:alpine
    user: "node"
    working_dir: /home/node/app
      - NODE_ENV=production
      - "3000:3000"
      - ./app:/home/node/app
      - ./node_modules:/home/node/node_modules
      - "3000"
    command: "node app.js"

Build, (re)create and start, the container in disconnected mode. The app folder is attached as a volume and mapped to the working directory, /home/node/app in the container. The node app.js command is executed in the containers working directory.

docker-compose up -d

Test the application locally in a browser at http://localhost:3000. Use Ctrl + C to kill the app after verifying it works.

Stop and remove the container and volumes created by docker-compose up.

docker-compose down

Build a Docker image for better performance and deployment once initial development is completed. Instead of mapping the local app and node_modules folders to the container, copy files and folders into the container, set working directories and run commands as needed.

Create this Dockerfile in the project root

FROM node:alpine
WORKDIR /home/node

# using wildcard (*) to copy both package.json and package-lock.json
COPY package*.json /home/node/
RUN yarn install --production

# create and set app directory as current dir
WORKDIR /home/node/app
COPY app/ /home/node/app/
CMD ["node", "app.js"]

Build the image and tag it. In the project root run the following command.

docker build -t myapp-node .

Test the new myapp-node Docker image using docker run. Same URL as before, http://localhost:3000.

docker run -u node -w /home/node/app -e NODE_ENV=production -p 3000:3000 --expose 3000 myapp-node node "app.js"

Stop the container using docker stop followed by the container ID. To get a list of all running containers, use docker ps --filter status=running.

That’s it!


A collection of useful Node.js code snippets and resources.

HTTP Server

An easy way to get a Node.js http server up and running is by installing the http-server node module globally, npm install http-server -g. Here is an example using the -a option to specify an address to use. For more options, run http-server --help.

http-server -a localhost
  • http-server default is to use address which is reachable by all IP addresses of the local machine’s network interface. Therefore, it may not be safe to run this address on a public network.

Handlebars Templates with Browserify

This post documents how to use the hbsfy precompiler plugin for Browserify to compile Handlebars templates into javascript. With this design, templates can be required as javascript modules in the views that use them.


Global Packages

List the global packages that are installed with the npm list –global command. Then install these if they are not already installed or need to be updated.

Install Browserify

After installing Node.js, using a Command shell, install Browserify globally. *

# global browserify install
npm install -g browserify

* If you have an older version of global Browserify, run npm rm –global browserify to remove the old version before installing the newer one.

Install Gulp

Install gulp globally with the npm install -g command as follows: *

# global gulp install
npm install -g gulp

* If you have an older version of global Gulp, run npm rm –global gulp to remove the old version before installing the newer one.

Project Packages

Create package.json

The package.json file contains project metadata and lists dependencies that are available on npm.

# create package.json
npm init

Install Packages

These are the project node module packages needed to browserify and bundle the Handlebars template modules. Since all of the modules are bundled into a single minified javascript file, source maps are included in the bundle task to make it easier to debug in the browser’s developer tools. For convenience, a local web server module has been included.

$ npm install browserify --save-dev
$ npm install handlebars --save-dev
$ npm install hbsfy --save-dev
$ npm install gulp --save-dev
$ npm install gulp-sourcemaps --save-dev
$ npm install gulp-uglify --save-dev
$ npm install gulp-util --save-dev
$ npm install gulp-webserver --save-dev
$ npm install vinyl-source-stream --save-dev
$ npm install vinyl-buffer --save-dev

Create a gulpfile.js in the project root to define and configure the Gulp tasks. The default task is simply gulp. When gulp is executed, it will run the browserify bundle task, start a local web server and open the default web page in a browser.

'use strict';

var gulp        = require('gulp'),
    browserify  = require('browserify'),
    buffer      = require('vinyl-buffer'),
    gutil       = require('gulp-util'),
    source      = require('vinyl-source-stream'),
    sourcemaps  = require('gulp-sourcemaps'),
    uglify      = require('gulp-uglify'),
    webserver   = require('gulp-webserver');

var hbsfy = require('hbsfy');

gulp.task('bundle', function () {
  var b = browserify({
    entries: './src/js/index.js',
    debug: true

  return b.transform(hbsfy).bundle()
    .pipe(sourcemaps.init({loadMaps: true}))
        .on('error', gutil.log)

// static web server
gulp.task('server', function() {
            port: 3000,
            directoryListing: false,
            open: true

gulp.task('default', ['bundle', 'server']);


Create a Handlebars template in /src/hbs/

{{#each people}}
<li>{{firstName}} {{lastName}}</li>

Create a data module in /src/js/modules/ for this template demo since we are not calling a rest API to get data.

module.exports = {
    people: [
        {firstName: "Yehuda", lastName: "Katz"},
        {firstName: "Carl", lastName: "Lerche"},
        {firstName: "Alan", lastName: "Johnson"}

In the /src/js folder, create the main node, index.js.

var data = require('./modules/data');
var list = require('../hbs/people.hbs');

document.querySelector('#people').innerHTML = list(data);

Web Page

Create this html file to load the javascript and render the template output.

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <title>Handlebars with Browserify</title>
    <ul id="people"></ul>
    <script src="js/bundle.js"></script>

Project Folders and Files

Here is a tree view of the projects folders and files. Browse, clone or download the source code if you prefer.

    • js
    • src
      • hbs
        • people.hbs
      • js
        • modules
          • data.js
        • index.js
    • gulpfile.js
    • package.json
    • index.html

The next page covers custom Handlebars helper registration

BrowserSync SSI Recipe

BrowserSync is my Node.js web browser app development server of choice. This gulpfile is a work in progress to address support for server side includes when using a BrowserSync static server.

var gulp = require('gulp');
var browserSync = require('browser-sync').create();
var minimist = require('minimist');

var serverConfig = {
    site: './src',
    directory: false,
    host: 'localhost',
    ssi: '.inc',
    index: 'index.html'

// from
// seems to make sense since gulp itself uses minimist.
var optionsConfig = {
    string: 'host',
    default: { host: process.env.NODE_ENV || }
var options = minimist(process.argv.slice(2), optionsConfig);

// Static server
gulp.task('server', function() {
        server: {
            index: serverConfig.index

// BrowserSync static server with ssi support
// ------------------------------------------------
// Known issues:
//   virtual include directives to an absolute URL path
//   do not work as expected. For example, given this file context,
//   http://localhost:3000/subdir/index.html
//     works:
//     <!--#include virtual="../incl/" -->
//     does NOT work correctly, include directives in this include may not be found:
//     <!--#include virtual="/incl/" -->
//     workaround: use gulp server-proxy task
// ------------------------------------------------
function ssiMiddleware (req, res, next) {

    var fs  = require('fs');
    var path = require('path');
    var ssi = require("ssi");
    var url = require('url');

    var pathname = url.parse(req.originalUrl || req.url).pathname;
    var filename = path.join(, pathname.substr(-1) === '/' ? pathname + serverConfig.index : pathname);

    if (fs.existsSync(filename)) {

        var matcher = '/**/*' + serverConfig.ssi;
        var parser = new ssi(,, matcher);

        var contents = parser.parse(filename, fs.readFileSync(filename, {
            encoding: 'utf8'

        res.writeHead(200, {
            'Content-Type': 'text/html'

        browserSync.emitter.on('service:running', function(data) {
            if (!snippet) {
                snippet = data.options.get('snippet');
    else {
gulp.task('server-ssi', function() {
        server: {
            index: serverConfig.index
        middleware: ssiMiddleware

// Proxy
// useful if apache needed for .htaccess, ssi, rewrite, etc.
// usage: $ gulp server-proxy --host [host]
// e.g.,  $ gulp server-proxy --host
gulp.task('server-proxy', function() {
Required Modules

$ gulp server-ssi

Bootstrap Customization

This tutorial covers using Git to clone, branch and merge the latest Bootstrap source code, modifying and compiling your modifications to the source LESS variables using Grunt and viewing your compiled changes with a local Node.js web server.

If you are not familiar with Git, Node.js and Grunt, please read my previous post, Grunt JavaScript Task Runner to help get you started.

You could just use the Bootstrap customize and download form to customize the LESS variables and download a pre-compiled Bootstrap package. However, as of this writing, the user interface at the form doesn’t provide a preview option to monitor your changes before you download the package. And since the Bootstrap version 3 source now includes Grunt, we can use it to compile the LESS source and preview the changes via localhost.

Source Code

Using Git, clone the Bootstrap repository. To keep our changes to the source separate and to make upgrading the bootstrap source easier, create a develop branch.

# git clone creates the bootstrap directory in your current directory
$ git clone

# create and checkout develop branch
$ git checkout -b develop

Load a Bootstrap example

I like to use the Node.js web-server.js that is bundled with the AngularJS tutorial. For example, here is how I start the NodeJs web-server.js that is included in the angular-phonecat repository on GitHub. This example presumes your CLI is in the bootstrap directory and the angular-phonecat repository is cloned to its own directory one level up.

UPDATED 1/4/2014

Repository contents rearranged over the holidays and the examples have been moved to the docs folder. You can always take a look at the latest source on GitHaub.

# directory structure (partial)
# | angular-phonecat/
#   | -- scripts/
#     | -- web-server.js
# | bootstrap/
#   | -- docs/
#     | -- examples/
#       | -- starter-template/
#         | -- index.html
#         | -- starter-template.css

# start the web server from the bootstrap directory
$ node ../angular-phonecat/scripts/web-server.js

Http Server running at http://localhost:8000/

Now open a web browser and go to http://localhost:8000/docs/examples/starter-template/index.html.

Modify LESS variables

Open the bootstrap/less/variables.less file in a text editor and find the @navbar-inverse-color and @navbar-inverse-link-color variables and change their values from @gray-light to @gray-lighter. Also, find @navbar-inverse-bg and change its value to #FF8C00.

Install node modules and compile LESS

The Bootstrap source contains a package.json for node module installation and a fully configured gruntfile.js to run Grunt tasks.

Open another bash window CLI and navigate to the bootstrap directory to install the node modules. After the node modules are installed, run the Grunt dist-css task which compiles the less files to dist/css/ followed by task dist-docs which copies the css files created in the previous task to docs/dist/css/.

# install node modules
$ npm install

# run grunt task to compile less to dist/css/
$ grunt dist-css

# run grunt task to copy to docs/dist/css/
$ grunt dist-docs

Refresh the web browser to see the compiled changes. The navigation bar at the top of the document in the starter template should now be orange instead of black.

Upgrading the source

Since we are only modifying the variables.less file from the source, upgrading is not that difficult. Using Git, merge the newer Bootstrap source master branch into the develop branch we created.

# checkout master branch
$ git checkout master

# get the latest bootstrap source
$ git pull origin master

# checkout develop
$ git checkout develop

# merge master into it
$ git merge master

Grunt JavaScript Task Runner

This tutorial describes how to setup and use the Grunt JavaScript task runner to automate repetitive tasks such as minification and compilation. Grunt is installed using npm, the Node.js package manager. You will also need Git to work with the tagged source code. This makes it easy to reset and compare your working copy of the code at each step. I discovered commit tags while using the AngularJS tutorial.

If you have not done so already, make sure that you have Node.js, Grunt CLI and Git installed. For Windows systems, after installing Git, you may decide to use the Git bash shell for your CLI (command line interface) instead of the Command Prompt.

Install Grunt’s command line interface (CLI) globally.

#install grunt CLI
$ npm install -g grunt-cli

Source Code

Using Git, clone the GruntTutorial repository. This contains all the source code for the tutorial. As you work through the tutorial, you will be instructed to use git to reset the source code so it matches the step at that point. This will revert the source code to it’s original state for the respective tag and thus overwrite any changes you have made to it.

# git clone creates the GruntTutorial directory in your current directory
$ git clone

# reset to step 0
$ git checkout -f step-00

Step 1: Node.js Package

# reset to step 1
$ git checkout -f step-01

A npm (nodejs package manager) package.json file is added to the project root for npm to read its structure and know what to do when installing it.

Update: 12-20-2014 — Interactively create a package.json file with the npm init command. More information available at the npm cli commands doc.

# create package.json
$ npm init

Grunt and node modules are installed per the package.json dependencies.

  "name": "grunt-tutorial",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-jshint": "~0.6.3",
    "grunt-contrib-nodeunit": "~0.2.0",
    "grunt-contrib-uglify": "~0.2.2"

These node_modules are already included in the step-01 source. To verify the process on your own, reset the tutorial source code to step-00 and run these npm install commands:

GruntTutorial – bash
# install the latest Grunt in your project folder
$ npm install grunt --save-dev

# install dependencies per package.json
$ npm install

Step 2: Combine and Minify Javascript files

# reset to step 2
$ git checkout -f step-02

Gruntfile.js is added to root of the project to specify the modules configuration, define tasks and load plugins. This Gruntfile.js specifies an uglify plugin to perform JavaScript minification. The banner option creates a comment on the first line of the minified file that is output. The JavaScript source (src) and destination (dest) paths are set in the build properties. Since our build source path has a wildcard * before the js filename extension, all of the js files in that directory will be minified into a single JavaScript file named grunt-tutorial.min.js as specified in the build destination property.

module.exports = function(grunt) {
  'use strict';
  // Project configuration.
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! <%= %> <%="yyyy-mm-dd") %> */\n'
      build: {
        src: 'src/js/*.js',
        dest: 'build/<%= %>.min.js'
  // Load the plugin that provides the "uglify" task.
  // Default task(s).
  grunt.registerTask('default', ['uglify']);

Time to test our Gruntfile.

# run the default task
$ grunt

You should recieve a message like this

Running "uglify:build" (uglify) task
File "build/js/grunt-tutorial.min.js" created.

Done, without errors. 

Step 3: Compass and Watch Plugin Install

# reset to step 3
$ git checkout -f step-03
OS X / Linux

For the Sass compile with the Compass & Watch Grunt plugins, you will need to have Ruby, Sass, and Compass version 0.12.2 or greater installed. Ruby comes pre-installed on OS X.


For the Sass compile with the Compass & Watch Grunt plugins, you will need to have the RubyInstaller for Windows. As of this writing, use Ruby 1.9.3 installers. These provide a stable language and a extensive list of packages (gems) that are compatible and updated. During the setup, check the option to Add Ruby executables to your PATH. After installing Ruby, install Sass and Compass using the Start Command Prompt with Ruby.

Start Command Prompt with Ruby
Start Command Prompt with Ruby

Install compass and watch Grunt plugins

By running these npm install commands with –save-dev, the package.json file will automatically be updated to include these two new dependencies.

GruntTutorial – bash
# install plugins and update package.json 
$ npm install grunt-contrib-compass --save-dev

$ npm install grunt-contrib-watch --save-dev

Compass and watch Grunt plugins added to the package.json devDependencies.

  "name": "grunt-tutorial",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.2",
    "grunt-contrib-jshint": "~0.6.3",
    "grunt-contrib-nodeunit": "~0.2.0",
    "grunt-contrib-uglify": "~0.2.2",
    "grunt-contrib-compass": "~0.6.0",
    "grunt-contrib-watch": "~0.4.4"

Step 4: Sass

# reset to step 4
$ git checkout -f step-04

Sass .scss files added to the source (src) directory and the Gruntfile.

module.exports = function(grunt) {
  'use strict';
  // Project configuration.
    pkg: grunt.file.readJSON('package.json'),
    compass: {
      dev: {
        // these options will override (or extend) config.rb settings.
        options: {  
          cssDir: 'build/css/',
          sassDir: 'src/sass/'
    uglify: {
      options: {
        banner: '/*! <%= %> <%="yyyy-mm-dd") %> */\n'
      build: {
        src: 'src/js/*.js',
        dest: 'build/js/<%= %>.min.js'
    watch: {
      css: {
        files: '**/*.scss',
        tasks: ['compass']
  // Load the plugins.
  // Default task(s).
  grunt.registerTask('default', ['uglify', 'watch']);

Time to test our Gruntfile.

# run the default task(s)
$ grunt

Step 5: Compile Sass

# reset to step 5
$ git checkout -f step-05

The CLI / Terminal shows the status of the Grunt tasks that are run.

# run the default task(s)
$ grunt

Running "uglify:build" (uglify) task
File "build/js/grunt-tutorial.min.js" created.

Running "watch" task

Edit and save one of the Sass (.scss) files. The watch task waits for changes to files and then fires a task. In our Gruntfile, the watch is configured to run the the compass task whenever a .scss file is updated. The compass task is configured to compile the Sass files specified in the sassDir property (src/sass/) and output to the cssDir specified (build/css/).

CLI / Terminal output from changed Sass file.

>> File "src/sass/reset.scss" changed.

Running "compass:dev" (compass) task
overwrite build/css/reset.css (0.02s)
unchanged src/sass/style.scss
Compilation took 0.053s

Done, without errors.
Completed in 0.717s at Sat Nov 23 2013 17:14:57 GMT-0500 (EST) - Waiting...
Source Code
December 2014 — New Page added to cover using a static web server with live browser reloading.