Docker: Node.js

How to use and deploy a Node.js app with Docker

👋 Welcome to the Stackhero documentation!

Stackhero offers a ready-to-use Docker cloud CaaS (Containers as a Service) solution that provides a host of benefits, including:

  • Easily deploy your containers to production with just a docker-compose up.
  • Customizable domain name secured with HTTPS (for example, https://api.your-company.com, https://www.your-company.com, https://backoffice.your-company.com).
  • Optimal performance and robust security powered by a private and dedicated VM.
  • Effortless updates with just a click.

Save time and simplify your life: it only takes 5 minutes to try Stackhero's Docker CaaS cloud hosting solution and deploy your containers to production!

This guide offers a solid foundation for developing a Node.js app and deploying it to production quickly and easily.

No prior knowledge of Docker is required. Everything is set up to ensure a smooth experience; all you need is Docker installed on your computer.

This documentation is designed for both beginners and experienced users who want to deploy a Node.js application using modern, scalable technologies without unnecessary complications.

Key features of this solution include:

  • 💪 Easy setup with minimal effort
  • 🐳 Utilises Docker for both development and production environments
  • 🔄 Automatic reloading of Node.js upon code changes using nodemon
  • 🚀 Single-command deployment to production
  • 🔒 TLS certificate management for secure HTTPS encryption
  • 🚧 Support for staging and preproduction platforms
  • 🧱 Modular and scalable architecture

If Docker is not yet installed on your computer, you can download it from the Docker official website. To verify that Docker is functioning correctly, open a terminal and run docker version. You should see version information without any errors.

After installing Docker, clone the following boilerplate repository:

git clone https://github.com/stackhero-io/nodejsWithDockerGettingStarted/
cd nodejsWithDockerGettingStarted

Then, start the development platform by running make development-start or view all available commands with make help.

To start the development platform, run:

make development-start

This command builds the Docker image, runs it, and executes the dev script defined in my-app/package.json (which is equivalent to running npm run dev).

In this example, a simple REST API using Express is created. You can view the API by navigating to http://localhost:5000. The page should display "Hello World".

Next, open the file my-app/src/app.js in your preferred IDE and modify the following line:

res.send('Hello World');

Change it to:

res.send('Updated!');

Save the file. The Node.js code will automatically reload and a refresh of http://localhost:5000 will reflect the updated API response.

Congratulations - you now have a fully operational development platform!

If you need to install additional packages, you can run make development-shell to access the container shell. Once inside, use NPM with npm install <package> or Yarn with yarn add <package> to install your desired packages.

If you have an existing Node.js project that you want to integrate with Docker, follow these steps:

  1. Create a new directory called my-app inside your project.

  2. Move all your project files into the my-app directory, excluding the .gitignore and .git files.

  3. Copy the docker, secrets, and Makefile from the boilerplate into the root directory of your project.

  4. Edit the .gitignore file in your project and add the following lines:

    node_modules/
    secrets/*.production
    secrets/*.staging
    

This boilerplate assumes your app listens on port 5000. If you prefer a different port, you can edit the docker/docker-compose.development.yml file and then relaunch the environment with make development-start.

If you wish to specify a different Node.js version, follow these steps:

  1. Open the file docker/my-app.dockerfile, which defines the Docker image for your app.
  2. Locate the first line that reads FROM node:<version>-alpine.
  3. Replace <version> with your chosen Node.js version. It is recommended to use the Long-Term Support (LTS) version. You can check the latest LTS version on the Node.js website. For example, to use the latest LTS version (currently 22), update the line to FROM node:22-alpine. If you prefer a specific version number, you can use something like FROM node:22.13.0-alpine.
  4. Save your changes to the Dockerfile.

Set environment variables for the development platform in the file secrets/my-app.development.

For production, use the secrets/my-app.production file.

Do not commit the secrets/my-app.production file to your Git repository! This file contains sensitive information and is ignored by default in the boilerplate's .gitignore to prevent accidental sharing.

If your Node.js app needs to store files (for example, user uploads), consider using an object storage service such as MinIO. An object storage service helps your application scale seamlessly while reducing potential issues.

If you prefer storing files locally, ensure you always use a Docker volume. Storing files directly in a container can lead to data loss. This boilerplate provides a volume mounted at /persistent for storing files safely.

Never store persistent data outside the /persistent directory unless you have created custom volumes and are certain of the configuration. Storing files outside /persistent will result in data loss!

You can easily modify this boilerplate to add a staging environment. To do so:

  1. Create a copy of docker/docker-compose.production.yml and name it docker/docker-compose.staging.yml. This file defines the containers and configuration for your staging environment.
  2. Create the secrets file secrets/my-app.staging containing any sensitive information required for staging, such as database passwords or API keys.
  3. In the Makefile, locate the section labelled "Staging platform" and uncomment it.

Finally, run make help to view the new staging commands that are now available.

If you do not yet have a Stackhero for Docker service, you can create one easily from your Stackhero dashboard. It will be activated within approximately 2 minutes.

If you are new to Stackhero, you can try the Docker container cloud hosting free for a month.

Before deploying your app to production, you need to prepare a few configuration files:

  1. Copy secrets/global.production.example to secrets/global.production.
  2. Edit secrets/global.production and replace <XXXXXX>.stackhero-network.com with your Docker service hostname from your Stackhero dashboard.
  3. Copy secrets/my-app.production.example to secrets/my-app.production.
  4. Edit secrets/my-app.production and insert your credentials.
  5. Update docker/docker-compose.production.yml by replacing <XXXXXX>.stackhero-network.com with your Docker service hostname.

Deploying to production is straightforward: run:

make production-deploy

This command creates a Docker container, transfers your project data, and sends it to your Docker service in production. Open your browser and navigate to your Docker service hostname (for example, https://<XXXXXX>.stackhero-network.com). You should see your REST API reply "Hello World".

You can also use make production, which deploys your containers and displays real-time logs.

To monitor your production environment or troubleshoot issues, you can view your logs using these commands:

  • To stream live logs, run: make production-logs-live
  • To retrieve all stored logs, run: make production-logs
  • To retrieve logs for a specific day (replace YYYY-MM-DD with the desired date), run: make production-logs | grep "YYYY-MM-DD"

If you wish to use a different domain name instead of https://<XXXXXX>.stackhero-network.com, Stackhero for Docker integrates Traefik to simplify domain management. Traefik handles HTTP routing and TLS encryption (HTTPS) for you.

Here are a couple of examples to customise your domain names:

  • To serve api.my-company.com via your container my-app on port 5000 with TLS encryption, update the docker/docker-compose.production.yml file by replacing the labels section with:

        labels:
          - "traefik.enable=true" # Enable Traefik to route traffic to this container
          - "traefik.http.routers.my-app.rule=Host(`api.my-company.com`)" # Define the host
          - "traefik.http.routers.my-app.tls.certresolver=letsencrypt" # Use letsencrypt for TLS certificates
          - "traefik.http.services.my-app.loadbalancer.server.port=5000" # Specify port 5000
    
  • To serve my-company.com via your container my-app on port 5000 and redirect all requests from www.my-company.com to my-company.com, update the labels section in the same file with:

        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.my-app.rule=Host(`my-company.com`) || Host(`www.my-company.com`)" # Include both domains
          - "traefik.http.routers.my-app.tls.certresolver=letsencrypt"
          - "traefik.http.services.my-app.loadbalancer.server.port=5000" # Specify port 5000
    
          # Redirect www.my-company.com to my-company.com:
          - "traefik.http.routers.my-app.middlewares=redirect-www"
          - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
          - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
          - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"
    

Do not forget to configure the DNS for my-company.com and www.my-company.com so that each points as a CNAME to your Docker service at https://<XXXXXX>.stackhero-network.com.