If you’ve ever searched for Docker + NodeJS
, you probably encountered a plethora of resources explaining how to containerize a Node.js
application. While following those guides step by step can be effective, I’m here to show you the quickest route to containerize your Node.js
app.
Let’s start with the Node.js
code you have. It’s a straightforward script that creates an HTTP
server listening on port 8080
. When a request hits this server, it responds with a simple message and an ASCII art of a whale cheerfully saying, “Hello from Docker!”
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(`
## .
## ## ## ==
## ## ## ## ## ===
/""""""""""""""""\\___/ ===
{ / ===-
\\______ O __/
\\ \\ __/
\\____\\_______/
Hello from Docker!
`);
res.end();
});
server.listen(8080, () => {
console.log('Server started!');
});
Let’s break it down further:
- Importing the HTTP Module:
const http = require('http');
Here, we’re importing the built-in NodeJS http
module, which allows us to create an HTTP server.
- Creating the Server:
const server = http.createServer((req, res) => { // Server logic goes here });
We create an HTTP server using the createServer
method provided by the http
module. This method takes a callback function as an argument, which is called every time the server receives a request. The req
parameter represents the request object, and res
represents the response object.
- Defining Request Handling Logic:
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(`
## .
## ## ## ==
## ## ## ## ## ===
/""""""""""""""""\\___/ ===
{ / ===-
\\______ O __/
\\ \\ __/
\\____\\_______/
Hello from Docker!
`);
res.end();
Inside the request handler callback function, we set the response headers using res.writeHead
, specifying a status code of 200
(which means “OK”) and the Content-Type
as text/plain
. Then, we use res.write
to write the response body. Here, we’re sending a multi-line string containing an ASCII art of a whale along with the text “Hello from Docker!”. Finally, we end the response using res.end()
.
- Starting the Server:
server.listen(8080, () => { console.log('Server started!'); });
We use the listen
method to start the server listening on port 8080
. When the server starts successfully, the callback function is executed, and it logs a message to the console saying “Server started!”.
Generating package.json
Let’s first generate package.json
npm install
The package.json
file serves a crucial purpose in Node.js development, particularly when working on larger projects or when collaborating with other developers. While it may seem unnecessary when running a simple script like app.js
, it provides several benefits:
- Dependency Management:
package.json
allows you to list all the dependencies your project needs. When you runnpm install
oryarn install
, Node.js will automatically install all the dependencies listed inpackage.json
. This is extremely helpful when your project grows and relies on multiple external libraries. - Version Control: By specifying the exact versions of your dependencies in
package.json
, you ensure that everyone working on the project uses the same versions. This helps prevent compatibility issues between different versions of dependencies. - Script Management:
package.json
lets you define custom scripts that can be executed usingnpm run <script-name>
. For example, you could have scripts for running tests, building your project, or deploying it to a server. - Metadata:
package.json
contains metadata about your project, such as its name, description, version, author, and license. This information is helpful for developers who want to understand your project quickly or for automated tools that may need to interact with it.
Running the Script
So, when you run this script, it creates an HTTP server that listens on port 8080
. Whenever you make a request to http://localhost:8080
in your browser or using a tool like a curl
, the server responds with the specified text along with the ASCII art of a whale saying “Hello from Docker!”.
node app.js
Server started!
You can now access the app using the curl command:
curl localhost:8080
## .
## ## ## ==
## ## ## ## ## ===
/""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/
Hello from Docker!
Containerising the Node.js Application
Now, let’s move on to containerizing our Node.js application. Traditionally, developers write a Dockerfile for this purpose. However, Docker offers a nifty tool called docker init
, automating much of the process.
Running docker init
guides us through creating essential Docker files tailored to our application. It even detects our application’s platform—Node.js in this case.
After specifying some details like Node.js
version, package manager (npm
in our case), startup command (node app.js
), and the port (8080
), docker init
generates Docker assets for us, including the Dockerfile
and Docker Compose
file.
Let’s test drive the tool:
docker init
Welcome to the Docker Init CLI!
This utility will walk you through creating the following files with sensible defaults for your project:
- .dockerignore
- Dockerfile
- compose.yaml
- README.Docker.md
Let's get started!
? What application platform does your project use? [Use arrows to move, type to filter]
> Node - (detected) suitable for a Node server application
Go - suitable for a Go server application
Python - suitable for a Python server application
Rust - suitable for a Rust server application
ASP.NET Core - suitable for an ASP.NET Core application
PHP with Apache - suitable for a PHP web application
Java - suitable for a Java application that uses Maven and packages as an uber jar
Other - general purpose starting point for containerizing your application
Don't see something you need? Let us know!
Quit
The beauty of docker init
is that it automatically detects your underlying application platform. In our demonstration, it detected that the application is based on Node.js.
? What version of Node do you want to use? (21.6.2)
The tool picked up 21.6.2 as per the best practices.
Next, it ask for which package manager do you want to leverage. Let’s pick up npm.
? Which package manager do you want to use? [Use arrows to move, type to filter]
> npm - (detected)
yarn
pnpm
Next, specify the command that you want to use. As we saw earlier, we used node <script_name> to bring up the HTTP server.
? What command do you want to use to start the app? [tab for suggestions] (node app.js)
Next, choose your preferred port. For our demonstration, we will pick up 8080.
? What port does your server listen on? 8080
Ensure that port 8080 is free and not occupied.
docker init
Welcome to the Docker Init CLI!
This utility will walk you through creating the following files with sensible defaults for your project:
- .dockerignore
- Dockerfile
- compose.yaml
- README.Docker.md
Let's get started!
? What application platform does your project use? Node
? What version of Node do you want to use? 21.6.2
? Which package manager do you want to use? npm
? What command do you want to use to start the app? node app.js
? What port does your server listen on? 8080
CREATED: .dockerignore
CREATED: Dockerfile
CREATED: compose.yaml
CREATED: README.Docker.md
✔ Your Docker files are ready!
Take a moment to review them and tailor them to your application.
When you're ready, start your application by running: docker compose up --build
Your application will be available at http://localhost:8080
Consult README.Docker.md for more information about using the generated files.
Verifying the Docker Assets
cat Dockerfile
# syntax=docker/dockerfile:1
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/
# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
ARG NODE_VERSION=21.6.2
FROM node:${NODE_VERSION}-alpine
# Use production node environment by default.
ENV NODE_ENV production
WORKDIR /usr/src/app
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.npm to speed up subsequent builds.
# Leverage a bind mounts to package.json and package-lock.json to avoid having to copy them into
# into this layer.
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=package-lock.json,target=package-lock.json \
--mount=type=cache,target=/root/.npm \
npm ci --omit=dev
# Run the application as a non-root user.
USER node
# Copy the rest of the source files into the image.
COPY . .
# Expose the port that the application listens on.
EXPOSE 8080
# Run the application.
CMD node app.js
Verifying the Compose File
services:
server:
build:
context: .
environment:
NODE_ENV: production
ports:
- 8080:8080
Running the Compose Services
docker compose up -d --build
docker compose up -d --build
[+] Building 11.3s (13/13) FINISHED docker:desktop-linux
=> [server internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.24kB 0.0s
=> [server] resolve image config for docker-...
1909ba6ef92861146998bc2b55c63e7ee85b7b52bc2d017f 0.0s
ing to docker.io/library/test-server:latest 0.0s
=> => unpacking to docker.io/library/test-server:latest 0.0s
[+] Running 1/2
⠹ Network test_default Created 0.3s
✔ Container test-server-1 Started
Did you notice that we didn’t even write Dockerfile or Docker Compose to bring up the Node.js application? That’s the power of docker init
command.
Conclusion
We’ve embarked on a journey to containerize a Node.js application using Docker. While traditional methods involve manual crafting of Dockerfiles and Docker Compose files, we explored a faster route with Docker’s docker init
command.
By leveraging docker init
, we automated much of the containerization process, allowing us to focus more on our application’s development and less on Docker configurations. This streamlined approach not only saves time but also simplifies the containerization workflow, making it accessible to developers of all skill levels.
As we navigate the seas of software development, tools like Docker
continue to evolve, offering new capabilities and efficiencies. Whether you’re containerizing a simple Node.js
script or a complex application, embracing automation tools like docker init
can help you sail smoothly towards your deployment goals.
So, as you continue your Docker voyage, remember the power of automation at your fingertips. And may your containers always sail swiftly and securely across the vast expanse of the digital ocean. Happy coding!
Latest Posts
-
Testcontainers and Playwright
Discover how Testcontainers-Playwright simplifies browser automation and testing without local Playwright installations. Learn about its features, limitations, compatibility, and usage with code examples.
-
Getting Started with the Low-Cost RPLIDAR Using NVIDIA Jetson Nano
Conclusion Getting started with low-code RPlidar with Jetson Nano is an exciting journey that can open up a wide range of possibilities for building robotics projects. In this blog post, we covered the basic steps to get started with low-code RPlidar with Jetson Nano, including setting up ROS, installing the RPlidar driver and viewing RPlidar…
-
Docker and Wasm Containers – Better Together
Learn how Docker Desktop and CLI both manages Linux containers and Wasm containers side by side.
-
Does Kubernetes Have a Future? A Technical Deep Dive and Analysis
Since its inception in 2014, Kubernetes has revolutionized the way we think about deploying and managing containerized applications. As the orchestrator of choice for many organizations, Kubernetes has become synonymous with cloud-native infrastructure. However, with the rapid evolution of technology and new paradigms emerging in the tech landscape, it’s fair to ask: does Kubernetes have…
-
Web Development with WebAssembly: A New Era of Performance and Portability
Web development has come a long way, but the journey is far from over. We’ve seen the rise of dynamic websites, responsive designs, and JavaScript-heavy applications that can do everything from playing video games to managing business data. But there’s always been one lingering challenge: performance. As web applications become more complex, achieving optimal performance…
-
How Cloud-Native Technologies Enhance Connectivity for Travelers in Indonesia
With its breathtaking landscapes and rich cultural heritage, Indonesia is a magnet for travelers. In a world where staying connected is as important as exploring new destinations, advancements in cloud-native technologies have transformed how tourists experience seamless connectivity. Solutions like the Indonesia eSIM offer a modern, hassle-free way to stay online, allowing travelers to share…