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.- Customisable 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 provides 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; you only need 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
Getting started
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.
Development platform
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!
Install packages
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.
Use an existing Node.js project
If you have an existing Node.js project that you want to integrate with Docker, follow these steps:
-
Create a new directory called
my-appinside your project. -
Move all your project files into the
my-appdirectory, excluding the.gitignoreand.gitfiles. -
Copy the
docker,secrets, andMakefilefrom the boilerplate into the root directory of your project. -
Edit the
.gitignorefile in your project and add the following lines:node_modules/ secrets/*.production secrets/*.staging
Opened port
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.
Choose a specific Node.js version
If you wish to specify a different Node.js version, follow these steps:
- Open the file
docker/my-app.dockerfile, which defines the Docker image for your app. - Locate the first line that reads
FROM node:<version>-alpine. - 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 toFROM node:22-alpine. If you prefer a specific version number, you can use something likeFROM node:22.13.0-alpine. - Save your changes to the Dockerfile.
Environment variables
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.productionfile to your Git repository! This file contains sensitive information and is ignored by default in the boilerplate's.gitignoreto prevent accidental sharing.
Store local files
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
/persistentdirectory unless you have created custom volumes and are certain of the configuration. Storing files outside/persistentwill result in data loss!
Add a staging environment
You can easily modify this boilerplate to add a staging environment. To do so:
- Create a copy of
docker/docker-compose.production.ymland name itdocker/docker-compose.staging.yml. This file defines the containers and configuration for your staging environment. - Create the secrets file
secrets/my-app.stagingcontaining any sensitive information required for staging, such as database passwords or API keys. - 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.
Production platform
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.
Prepare your first deploy to production
Before deploying your app to production, you need to prepare a few configuration files:
- Copy
secrets/global.production.exampletosecrets/global.production. - Edit
secrets/global.productionand replace<XXXXXX>.stackhero-network.comwith your Docker service hostname from your Stackhero dashboard. - Copy
secrets/my-app.production.exampletosecrets/my-app.production. - Edit
secrets/my-app.productionand insert your credentials. - Update
docker/docker-compose.production.ymlby replacing<XXXXXX>.stackhero-network.comwith your Docker service hostname.
Deploy to production
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.
Display 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-DDwith the desired date), run:make production-logs | grep "YYYY-MM-DD"
Customise domain names
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.comvia your containermy-appon port 5000 with TLS encryption, update thedocker/docker-compose.production.ymlfile by replacing thelabelssection 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.comvia your containermy-appon port 5000 and redirect all requests fromwww.my-company.comtomy-company.com, update thelabelssection 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.comandwww.my-company.comso that each points as a CNAME to your Docker service athttps://<XXXXXX>.stackhero-network.com.