├── scripts ├── run ├── rebuild ├── shell ├── open └── README.md ├── Procfile ├── Dockerfile ├── app.json ├── test └── index.js ├── package.json ├── circle.yml ├── app.js ├── docker-compose.yml ├── LICENSE └── README.md /scripts/run: -------------------------------------------------------------------------------- 1 | docker-compose up web 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm install; node app.js 2 | -------------------------------------------------------------------------------- /scripts/rebuild: -------------------------------------------------------------------------------- 1 | docker-compose build 2 | -------------------------------------------------------------------------------- /scripts/shell: -------------------------------------------------------------------------------- 1 | docker-compose run --service-ports web bash 2 | -------------------------------------------------------------------------------- /scripts/open: -------------------------------------------------------------------------------- 1 | open "http://$(docker-machine ip default):8080" 2 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # scripts 2 | Just some scripts to shorten the docker commands. 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM heroku/nodejs 2 | RUN apt-get update && apt-get install -y postgresql-client-9.3 3 | 4 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-heroku-docker", 3 | "description": "A simple docker heroku example for nodejs", 4 | "image": "heroku/nodejs", 5 | "addons": [] 6 | } 7 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | 3 | describe("heroku-docker-nodejs", function() { 4 | it("should pass test :)", function() { 5 | assert(true); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heroku-docker-nodejs", 3 | "private": true, 4 | "version": "0.0.0", 5 | "dependencies": { 6 | "express": "^4.13.3", 7 | "mocha": "^2.3.3" 8 | }, 9 | "scripts": { 10 | "test": "mocha test/index.js" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | services: 3 | - docker 4 | 5 | dependencies: 6 | pre: 7 | - sudo pip install --upgrade docker-compose==1.3.0 8 | override: 9 | - docker-compose build 10 | 11 | test: 12 | override: 13 | - docker-compose run --service-ports web npm test 14 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.get('/', function (req, res) { 5 | res.send("Hello from container land!"); 6 | }); 7 | 8 | var server = app.listen(process.env.PORT, function () { 9 | var port = server.address().port; 10 | console.log('Example app listening at http://localhost:%s', port); 11 | }); 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | web: 2 | build: . 3 | command: 'bash -c ''npm install; node app.js''' 4 | working_dir: /app/user 5 | volumes: 6 | - ./:/app/user/ 7 | - /app/user/node_modules 8 | environment: 9 | PORT: 8080 10 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres' 11 | ports: 12 | - '8080:8080' 13 | links: 14 | - herokuPostgresql 15 | shell: 16 | build: . 17 | command: bash 18 | working_dir: /app/user 19 | environment: 20 | PORT: 8080 21 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres' 22 | ports: 23 | - '8080:8080' 24 | links: 25 | - herokuPostgresql 26 | volumes: 27 | - '.:/app/user' 28 | herokuPostgresql: 29 | image: postgres 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jamie Blair 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heroku-docker-nodejs 2 | A simple heroku-docker example for [nodejs](https://nodejs.org) with development, CI and deployment instructions 3 | 4 | This README acts as a quick start guide to setting up and using heroku-docker, the repo itself contains the final result. 5 | 6 | [![circleci](https://circleci.com/gh/orangemug/heroku-docker-nodejs.png?style=shield)][circleci] 7 | [![Dependency Status](https://david-dm.org/orangemug/heroku-docker-nodejs.svg)][dm-prod] 8 | [![Dev Dependency Status](https://david-dm.org/orangemug/heroku-docker-nodejs/dev-status.svg)][dm-dev] 9 | 10 | [circleci]: https://circleci.com/gh/orangemug/heroku-docker-nodejs 11 | [dm-prod]: https://david-dm.org/orangemug/heroku-docker-nodejs 12 | [dm-dev]: https://david-dm.org/orangemug/heroku-docker-nodejs#info=devDependencies 13 | 14 | 15 | ## Prerequisites 16 | Install some deps 17 | 18 | * [docker-compose](https://docs.docker.com/compose/install) 19 | * [heroku toolbelt](https://toolbelt.heroku.com/) 20 | 21 | Via the CLI, install the `heroku-docker` plugin 22 | 23 | heroku plugins:install heroku-docker 24 | 25 | We'll also assume you'll have this code in a repo on [github](http://github.com) 26 | 27 | 28 | ## Setup 29 | Create a `package.json` 30 | 31 | { 32 | "name": "heroku-docker-nodejs", 33 | "private": true, 34 | "version": "0.0.0", 35 | "dependencies": { 36 | "express": "^4.13.3" 37 | } 38 | } 39 | 40 | ...an `app.js` 41 | 42 | var express = require('express'); 43 | var app = express(); 44 | 45 | app.get('/', function (req, res) { 46 | res.send("Hello from container land!"); 47 | }); 48 | 49 | var server = app.listen(process.env.PORT, function () { 50 | var port = server.address().port; 51 | console.log('Example app listening at http:/localhost:%s', port); 52 | }); 53 | 54 | ...an app.json describing your app 55 | 56 | { 57 | "name": "nodejs-heroku-docker", 58 | "description": "A simple docker heroku example for nodejs", 59 | "image": "heroku/nodejs", 60 | "addons": [] 61 | } 62 | 63 | ...and a `Procfile` to start our app with[^](https://ddollar.github.io/foreman) 64 | 65 | web: npm install; node app.js 66 | 67 | Now lets get the heroku toolbelt to generate us some heroku config 68 | 69 | $ heroku docker:init 70 | Wrote Dockerfile 71 | Wrote docker-compose.yml 72 | 73 | This will have created 2 files which _docker-compose_ uses to create and run your containers. Now go and add some additional bits to your `docker-compose.yml`, this will allow you to share files between the host and the container during dev. 74 | 75 | web: 76 | volumes: 77 | - ./:/app/user/ 78 | - /app/user/node_modules 79 | 80 | Then run 81 | 82 | $ docker-compose build 83 | 84 | Note if the `Dockerfile` has changed then you'll have to rerun the above command, to rebuild the container 85 | 86 | 87 | ## Development 88 | When running the app in development the main requirement is quick restarts of your app. So instead of reloading the VM each time, we'll start a bash shell in the container and run the app manually. 89 | 90 | $ docker-compose run --service-ports web bash 91 | 92 | The important part above is `docker-compose run web` which will run a command in our docker container. `--service-ports` tells _docker-compose_ that we want to setup the port mappings. You should now be seeing a bash prompt where you can start your app. 93 | 94 | root@52697b69237b:/code# npm install 95 | root@52697b69237b:/code# npm start 96 | 97 | Open another terminal and run the following to hit the web server from a browser 98 | 99 | $ open "http://$(docker-machine ip default):8080" 100 | 101 | 102 | ## Deployment 103 | To deploy the VM to production first off create an app on heroku 104 | 105 | heroku create [optional name] 106 | 107 | Now instead of running `git push heroku` as you'd normally do with heroku, we instead run 108 | 109 | heroku docker:release 110 | 111 | As normal you can now open the app in your browser 112 | 113 | heroku open 114 | 115 | 116 | ## Testing 117 | Next up lets setup some CI with [circleci](http://circleci.com), first off [create an account](https://circleci.com/signup) if you haven't already got one. 118 | 119 | Create a simple test (ok it doesn't actually test anything but you'll get the idea) 120 | 121 | // ./test/index.js 122 | var assert = require("assert"); 123 | 124 | describe("heroku-docker-nodejs", function() { 125 | it("should pass test :)", function() { 126 | assert(true); 127 | }); 128 | }); 129 | 130 | Add the test script and `mocha` dependency to your `package.json` 131 | 132 | { 133 | "dependencies": { 134 | "mocha": "^2.3.3" 135 | }, 136 | "scripts": { 137 | "test": "mocha test/index.js" 138 | } 139 | } 140 | 141 | Next up add a `circle.yml` to the base of the repo and _push this to github_ 142 | 143 | machine: 144 | services: 145 | - docker 146 | 147 | dependencies: 148 | pre: 149 | - sudo pip install --upgrade docker-compose==1.3.0 150 | override: 151 | - docker-compose build 152 | 153 | test: 154 | override: 155 | - docker-compose run --service-ports web npm test 156 | 157 | Then [add the project](https://circleci.com/add-projects) in circleci and you should very shortly see passing test :) 158 | 159 | 160 | ## Add ons 161 | We'll add postgres as an example here as it's probably the most common addon for heroku. First off add the following to your `docker-compose.yml` 162 | 163 | web: 164 | environment: 165 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres' 166 | links: 167 | - herokuPostgresql 168 | shell: 169 | environment: 170 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres' 171 | links: 172 | - herokuPostgresql 173 | # This defines a new service called herokuPostgresql 174 | herokuPostgresql: 175 | image: postgres 176 | 177 | `herokuPostgresql` defines a new service which uses the [postgres image from dockerhub](https://hub.docker.com/_/postgres/), this is linked from the web/shell services. When you setup a link the service will be added to `/etc/hosts` 178 | 179 | So the host `herokuPostgresql` will point to our postgres server running in another container, cool huh! 180 | 181 | Now we'll add `psql` so we can connect to postgres from within our `web` service. To do this append the following to the `Dockerfile` 182 | 183 | RUN apt-get update && apt-get install -y postgresql-client-9.3 184 | 185 | Now rebuild the image 186 | 187 | docker-compose build 188 | 189 | We can now start a shell and test that we can connect to postgres. 190 | 191 | $ docker-compose run --service-ports web bash 192 | $ psql $DATABASE_URL 193 | 194 | Note: The `DATABASE_URL` was defined in the `docker-compose-yml` above. 195 | 196 | You should now be connected to the postgres server! If you want to know more about the postgres docker image see 197 | 198 | 199 | ## Extras 200 | I've also added a few scripts (just shorthands really) to the [./scripts](./scripts) directory 201 | 202 | * [shell](./scripts/shell) - to run in development 203 | * [open](./scripts/open) - to open in a browser 204 | * [rebuild](./scripts/rebuild) - rebuild the container 205 | * [run](./scripts/run) - to run in a production like way 206 | 207 | 208 | ## References 209 | 210 | * 211 | * 212 | * 213 | * 214 | 215 | 216 | ## Thanks! 217 | To [samgiles](https://github.com/samgiles) and [oliverbrooks](https://github.com/oliverbrooks) for the pointers 218 | 219 | 220 | ## License 221 | [MIT](LICENSE) 222 | --------------------------------------------------------------------------------