• Stars
    star
    212
  • Rank 186,122 (Top 4 %)
  • Language
    Shell
  • License
    MIT License
  • Created over 3 years ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An example Node / Express app that's using Docker and Docker Compose.

An example Node + Docker app

CI

You could use this example app as a base for your new project or as a guide to Dockerize your existing Node app.

The example app is minimal but it wires up a number of things you might use in a real world Node app, but at the same time it's not loaded up with a million personal opinions.

For the Docker bits, everything included is an accumulation of Docker best practices based on building and deploying dozens of assorted Dockerized web apps since late 2014.

This app is using Express 4.18.2 and Node 18.15.0. The screenshot doesn't get updated every time I bump the versions:

Screenshot

Table of contents

Tech stack

If you don't like some of these choices that's no problem, you can swap them out for something else on your own.

Back-end

Front-end

But what about client side JavaScript?!

Picking a JS library is a very app specific decision because it depends on which library you like and it also depends on if your app is going to be mostly EJS templates with sprinkles of JS or an API back-end.

This isn't an exhaustive list but here's a few reasonable choices depending on how you're building your app:

On the bright side with esbuild being set up you can use any (or none) of these solutions very easily. You could follow a specific library's installation guides to get up and running in no time.

Personally I'm going to be using Hotwire Turbo + Stimulus in most newer projects.

Notable opinions and packages

Express is a very very unopinionated framework and let me preface this by saying I'm not primarily a Node developer. Most of these design choices are based on experience using other web frameworks and Googling for Node specifics.

If you find yourself face palming or see a way to improvement something please open an issue or PR.

  • Packages and middleware:

    • morgan for logging HTTP requests
    • express-ejs-layouts for using EJS with layouts
    • nodemon for code reloading in development
    • Redis based session middleware is enabled
    • Static file middleware is enabled
  • Linting and testing:

    • eslint is used to lint the code base using airbnb-base
    • jest for writing tests and reporting test coverage
  • Routes:

    • Add page route to render a home page
    • Add up route to provide a few health check pages
  • Config:

    • Log to STDOUT so that Docker can consume and deal with log output
    • Extract a bunch of configuration settings into environment variables
    • backend/config/index.js and the .env file handles configuration in all environments
  • Front-end assets:

    • frontend/ contains all your CSS, JS, images, fonts, etc. and is managed by esbuild
    • Custom 502.html and maintenance.html pages
    • Generate favicons using modern best practices
  • Express defaults that are changed:

    • public/ is the static directory where Express will serve static files from

Besides the Node app itself:

  • Docker support has been added which would be any files having *docker* in its name
  • GitHub Actions have been set up

Running this app

You'll need to have Docker installed. It's available on Windows, macOS and most distros of Linux. If you're new to Docker and want to learn it in detail check out the additional resources links near the bottom of this README.

You'll also need to enable Docker Compose v2 support if you're using Docker Desktop. On native Linux without Docker Desktop you can install it as a plugin to Docker. It's been generally available for a while now and very stable. This project uses a specific Docker Compose profiles feature that only works with Docker Compose v2.

If you're using Windows, it will be expected that you're following along inside of WSL or WSL 2. That's because we're going to be running shell commands. You can always modify these commands for PowerShell if you want.

Clone this repo anywhere you want and move into the directory:

git clone https://github.com/nickjj/docker-node-example hellonode
cd hellonode

# Optionally checkout a specific tag, such as: git checkout 0.6.0

Copy an example .env file because the real one is git ignored:

cp .env.example .env

Build everything:

The first time you run this it's going to take 5-10 minutes depending on your internet connection speed and computer's hardware specs. That's because it's going to download a few Docker images and build the Node + Yarn dependencies.

docker compose up --build

Now that everything is built and running we can treat it like any other Node app.

Did you receive an error about a port being in use? Chances are it's because something on your machine is already running on port 8000. Check out the docs in the .env file for the DOCKER_WEB_PORT_FORWARD variable to fix this.

Did you receive a permission denied error? Chances are you're running native Linux and your uid:gid aren't 1000:1000 (you can verify this by running id). Check out the docs in the .env file to customize the UID and GID variables to fix this.

Check it out in a browser:

Visit http://localhost:8000 in your favorite browser.

Linting the code base:

# You should get no output (that means everything is operational).
./run lint

We'll go over that ./run script in a bit!

Running the test suite:

# You should see all passing tests. Warnings are typically ok.
./run test

Stopping everything:

# Stop the containers and remove a few Docker related resources associated to this project.
docker compose down

You can start things up again with docker compose up and unlike the first time it should only take seconds.

Files of interest

I recommend checking out most files and searching the code base for TODO:, but please review the .env and run files before diving into the rest of the code and customizing it. Also, you should hold off on changing anything until we cover how to customize this example app's name with an automated script (coming up next in the docs).

.env

This file is ignored from version control so it will never be commit. There's a number of environment variables defined here that control certain options and behavior of the application. Everything is documented there.

Feel free to add new variables as needed. This is where you should put all of your secrets as well as configuration that might change depending on your environment (specific dev boxes, CI, production, etc.).

run

You can run ./run to get a list of commands and each command has documentation in the run file itself.

It's a shell script that has a number of functions defined to help you interact with this project. It's basically a Makefile except with less limitations. For example as a shell script it allows us to pass any arguments to another program.

This comes in handy to run various Docker commands because sometimes these commands can be a bit long to type. Feel free to add as many convenience functions as you want. This file's purpose is to make your experience better!

If you get tired of typing ./run you can always create a shell alias with alias run=./run in your ~/.bash_aliases or equivalent file. Then you'll be able to run run instead of ./run.

Running a script to automate renaming the project

The app is named hello right now but chances are your app will be a different name. Since the app is already created we'll need to do a find / replace on a few variants of the string "hello" and update a few Docker related resources.

And by we I mean I created a zero dependency shell script that does all of the heavy lifting for you. All you have to do is run the script below.

Run the rename-project script included in this repo:

# The script takes 2 arguments.
#
# The first one is the lower case version of your app's name, such as myapp or
# my_app depending on your preference.
#
# The second one is used for your app's module name. For example if you used
# myapp or my_app for the first argument you would want to use MyApp here.
bin/rename-project myapp MyApp

The bin/rename-project script is going to:

  • Remove any Docker resources for your current project
  • Perform a number of find / replace actions
  • Optionally initialize a new git repo for you

Afterwards you can delete this script because its only purpose is to assist in helping you change this project's name without depending on any complicated project generator tools or 3rd party dependencies.

If you're not comfy running the script or it doesn't work for whatever reasons you can check it out and perform the actions manually. It's mostly running a find / replace across files and then renaming a few directories and files.

Start and setup the project:

This won't take as long as before because Docker can re-use most things.

docker compose up --build

Sanity check to make sure the tests still pass:

It's always a good idea to make sure things are in a working state before adding custom changes.

# Then in a 2nd terminal once it's up and ready.
./run lint
./run test

If everything passes now you can optionally git add -A && git commit -m "Initial commit" and start customizing your app. Alternatively you can wait until you develop more of your app before committing anything. It's up to you!

Tying up a few loose ends:

You'll probably want to create a fresh CHANGELOG.md file for your project. I like following the style guide at https://keepachangelog.com/ but feel free to use whichever style you prefer.

Since this project is MIT licensed you should keep my name and email address in the LICENSE file to adhere to that license's agreement, but you can also add your name and email on a new line.

If you happen to base your app off this example app or write about any of the code in this project it would be rad if you could credit this repo by linking to it. If you want to reference me directly please link to my site at https://nickjanetakis.com. You don't have to do this, but it would be very much appreciated!

Updating dependencies

Without Docker you'd normally run yarn install from either your backend/ or frontend/ directory. With Docker it's basically the same thing and since these commands are in our Dockerfile we can get away with doing a docker compose build but don't run that just yet.

In development:

You can run ./run yarn:outdated or ./run yarn:outdated:frontend to get a list of outdated dependencies based on what you currently have installed. Once you've figured out what you want to update, go make those updates in your backend/package.json and / or frontend/package.json file.

Then to update your dependencies you can run ./run yarn:install or ./run yarn:install:frontend. That'll make sure any lock files get copied from Docker's image (thanks to volumes) into your code repo and now you can commit those files to version control like usual.

You can check out the run file to see what these commands do in more detail.

In CI:

You'll want to run docker compose build since it will use any existing lock files if they exist. You can also check out the complete CI test pipeline in the run file under the ci:test function.

In production:

This is usually a non-issue since you'll be pulling down pre-built images from a Docker registry but if you decide to build your Docker images directly on your server you could run docker compose build as part of your deploy pipeline.

See a way to improve something?

If you see anything that could be improved please open an issue or start a PR. Any help is much appreciated!

Additional resources

Now that you have your app ready to go, it's time to build something cool! If you want to learn more about Docker, Node and deploying a Node app here's a couple of free and paid resources. There's Google too!

Learn more about Docker and Node

Official documentation

Courses

Deploy to production

I'm creating an in-depth course related to deploying Dockerized web apps. If you want to get notified when it launches with a discount and potentially get free videos while the course is being developed then sign up here to get notified.

About the author

I'm a self taught developer and have been freelancing for the last ~20 years. You can read about everything I've learned along the way on my site at https://nickjanetakis.com.

There's hundreds of blog posts and a couple of video courses on web development and deployment topics. I also have a podcast where I talk with folks about running web apps in production.

More Repositories

1

docker-django-example

A production ready example Django app that's using Docker and Docker Compose.
Python
1,206
star
2

docker-rails-example

A production ready example Rails app that's using Docker and Docker Compose.
Ruby
987
star
3

build-a-saas-app-with-flask

Learn how to build a production ready web app with Flask and Docker.
HTML
959
star
4

dotfiles

Settings for various tools I use.
Shell
938
star
5

ansible-docker

Install / Configure Docker and Docker Compose using Ansible.
Python
750
star
6

orats

Opinionated rails application templates.
Ruby
665
star
7

docker-flask-example

A production ready example Flask app that's using Docker and Docker Compose.
Python
634
star
8

ansigenome

A tool to help you gather information and manage your Ansible roles.
Python
449
star
9

flask-webpack

A Flask extension to manage assets with Webpack.
Python
338
star
10

rolespec

A test library for testing Ansible roles
Shell
232
star
11

docker-web-framework-examples

Example apps that demonstate how to use Docker with your favorite web frameworks.
Elixir
218
star
12

docker-phoenix-example

A production ready example Phoenix app that's using Docker and Docker Compose.
Elixir
211
star
13

flask-static-digest

Flask extension to help make your static files production ready by md5 tagging and gzipping them.
Python
156
star
14

notes

A zero dependency shell script that makes it really simple to manage your text notes.
Shell
129
star
15

manifest-revision-webpack-plugin

Write out a manifest file containing your versioned webpack chunks and assets.
JavaScript
124
star
16

flask-db

A Flask CLI extension to help migrate and manage your SQL database.
Python
76
star
17

ansible-nginx

Install and configure nginx (SSL A+ by default) with Ansible.
Jinja
73
star
18

ansible-acme-sh

Install and auto-renew SSL certificates with Let's Encrypt using acme.sh.
71
star
19

wait-until

A zero dependency Bash script that waits until a command of your choosing has run successfully.
Shell
57
star
20

webserver

A zero dependency Python 3 web server to echo back an HTTP request's headers and data.
Python
50
star
21

dockercon21-docker-best-practices

Reference links for my live demo talk from DockerCon 21.
49
star
22

ansible-user

Create and configure a user for SSH key based logins and passwordless sudo.
48
star
23

ansible-fail2ban

Install and configure fail2ban using ansible.
46
star
24

runninginproduction.com

The website for the Running in Production podcast.
HTML
41
star
25

esbuild-copy-static-files

An esbuild plugin to copy static files that changed from a source directory to a destination directory.
Shell
37
star
26

flask-pg-extras

A Flask extension to obtain useful information from your PostgreSQL database.
Python
33
star
27

ansible-swapfile

Create and configure a swap file with Ansible.
32
star
28

ansible-letsencrypt

Install and auto-renew SSL certificates with Let's Encrypt and Ansible.
Python
29
star
29

invoice

Calculate a billable amount, hours and days logged for 1 or more projects.
Shell
18
star
30

ansible-playbooks

A collection of ansible playbooks with end to end examples.
Shell
18
star
31

nyhackr-cli-dev-env

Reference notes for the Creating a Command Line Driven Development Environment talk.
18
star
32

ansible-rails

Deploy a rails application using git with ansible.
Ruby
18
star
33

gowatcher

Reload a specified go program automatically by monitoring a directory.
Shell
16
star
34

docker-community-all-hands

Reference links to every talk I've given for the Docker Community All-Hands events.
15
star
35

deploy-web-apps-with-docker

Rescue yourself from the complexity of DevOps
Dockerfile
15
star
36

title-case-converter

A CLI tool to capitalize words based on industry standard style guides.
Python
14
star
37

sublime-text-3-packages

A list of my Sublime Text 3 packages along with their settings.
Python
14
star
38

flask-secrets

A Flask CLI extension to generate random secret tokens.
Python
14
star
39

verdiff

A CLI tool to diff 2 versions of a Phoenix, Rails, Django or Laravel generated project.
Python
12
star
40

ansible-security

Configure ssh and ufw as well as install fail2ban with ansible.
12
star
41

gemshine

Recursively compare a ruby project's gem versions to their latest versions.
Ruby
12
star
42

ansible-iptables

Configure iptables using Ansible.
12
star
43

lcurl

Visit a site every X seconds in a loop to help detect downtime while testing deployment strategies.
Shell
12
star
44

ansible-sshd

Install and configure openssh-server using Ansible.
12
star
45

demo-for-chattanooga-python-user-group

A demo app for a talk I gave at the Chattanooga python user group.
JavaScript
10
star
46

latest-releases

A command line tool that lets you keep tabs on the latest releases of your favorite tools and libraries.
Shell
10
star
47

ansible-monit

Install monit and configure as many processes as you want with ansible.
8
star
48

passify

A small utility to create a password and wrap bcrypt.
JavaScript
8
star
49

pick-random-youtube-comments

Get a list of top level comments from a YouTube video and then pick N amount of unique comment authors by choosing them randomly.
Python
8
star
50

ansible-bootstrap

Configure a server to run Ansible and install essential packages.
6
star
51

ansible-ferm

Manage iptables with ferm using ansible.
5
star
52

ansible-postgres

Install a bare bones version of postgres with ansible.
5
star
53

ansible-pumacorn

Manage a puma or unicorn rails process with init.d using ansible.
Ruby
5
star
54

docker-faye

A docker image for running a secure Faye (websocket) server.
JavaScript
5
star
55

ansible-nodejs

Install the latest stable version of nodejs with ansible.
4
star
56

ansible-mariadb

Install and configure MariaDB using Ansible.
4
star
57

ansible-sendy

Copy and configure Sendy with Ansible.
C
3
star
58

ansible-locale

Install and configure your system's locale using ansible
3
star
59

ansible-phpfpm

Install and configure php-fpm using Ansible.
2
star
60

ansible-dnsmasq

Install and configure dnsmasq to map a TLD to localhost using ansible.
2
star
61

docker-play-example

A production ready example Play app that's using Docker and Docker Compose.
1
star