Getting Started with the Node Chainguard Image

Tutorial on how to get started with the Node Chainguard Image

The Node Chainguard Image is a distroless container image that has the tooling necessary to build and execute Node applications, including npm.

In this guide, we’ll set up a demo application and create a Dockerfile to build and execute the demo using the Node Chainguard Image as base.

This tutorial requires Docker, Node, and Npm to be installed on your local machine.

What is distroless Distroless images are minimalist container images containing only essential software required to build or execute an application. That means no package manager, no shell, and no bloat from software that only makes sense on bare metal servers.
What is Wolfi Wolfi is a community Linux undistro created specifically for containers. This brings distroless to a new level, including additional features targeted at securing the software supply chain of your application environment: comprehensive SBOMs, signatures, daily updates, and timely CVE fixes.
Chainguard Images Chainguard Images are a mix of distroless and development images based on Wolfi. Nightly builds make sure images are up-to-date with the latest package versions and patches from upstream Wolfi.

Step 1: Setting up a Demo Application

We’ll start by creating a small Node application to serve as a demo. This application uses a mock server to test GET and POST requests, and is based on the Docker Node example.

First, create a directory for your app. In this guide we’ll use wolfi-node:

mkdir ~/wolfi-node && cd ~/wolfi-node

Run the following command to create a new package.json file.

npm init -y

Exit the init process. You do not need to fill out the metadata as this is a demo.

Install npm:

npm install

Next, install the application dependencies. We’ll need ronin-server and ronin-mocks, which are used together to create a “mock” server that saves JSON data in memory and returns it in subsequent GET requests to the same endpoint.

npm install ronin-server ronin-mocks

Using your code editor of choice, create a new file named server.js for your application. Here we’ll use nano:

nano server.js

Copy the following code to your server.js file:

const ronin = require('ronin-server')
const mocks = require('ronin-mocks')

const server = ronin.server()

server.use('/', mocks.server(server.Router(), false, true))

Save the file when you’re done. Then, run the server with:

node server.js

This command will start the application and wait for connections on port 8000. The mocking server will save any JSON data submitted by a POST request

From a new terminal window, run the following command, which will make a POST request to your application sending a JSON payload:

curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing" }'

When the connection is successful, you should get output like this on the terminal that is running the application:

2023-02-07T15:48:54:2450  INFO: POST /test

You can now test that the content was saved by running a GET request to the same endpoint:

curl http://localhost:8000/test

You’ll get output similar to this:


The demo application is now ready. In the next step, you’ll create a Dockerfile to run your app. Before moving along, make sure to stop the server running on your terminal by typing CTRL+C (or CMD+C for macOS users).

Step 2: Creating the Dockerfile

In your code editor of choice, create a new Dockerfile:

nano Dockerfile

The following Dockerfile will:

  1. Start a new image based on the image;
  2. Set the work dir to /app inside the container;
  3. Copy application files from the current directory to the /app location in the container;
  4. Run npm install to install production-only dependencies;
  5. Set up additional arguments to the default entrypoint (node), specifying which script to run.

Copy this content to your own Dockerfile:

ENV NODE_ENV=production


COPY --chown=node:node ["package.json", "package-lock.json", "server.js", "./"]

RUN npm install --omit-dev

CMD [ "server.js" ]

Save the file when you’re finished.

You can now build the image with:

docker build . -t wolfi-node-server

Once the build is finished, run the image with:

docker run --rm -it -p 8000:8000 wolfi-node-server

And it should work the same way as before: the terminal will be blocked with the application waiting for connections on port 8000. The difference is that this is running inside the container, so we set up a port redirect to receive requests on localhost:8000.

curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing node wolfi image" }'

You can now query the same endpoint to receive the data that was stored in memory when you run the previous command:

curl http://localhost:8000/test
{"code":"success","meta":{"total":1,"count":1},"payload":[{"msg":"testing node wolfi image","id":"6011f987-b9f8-4442-8253-d54166df5966","createDate":"2023-02-07T15:57:23.520Z"}]}

Advanced Usage

If your project requires a more specific set of packages that aren't included within the general-purpose Node Chainguard Image, you'll first need to check if the package you want is already available on the wolfi-os repository.

Note: If you're building on top of an image other than the wolfi-base image, the image will run as a non-root user. Because of this, if you need to install packages with apk install you need to use the USER root directive.

If the package is available, you can use the wolfi-base image in a Dockerfile and install what you need with apk, then use the resulting image as base for your app. Check the "Using the wolfi-base Image" section of our images quickstart guide for more information.

If the packages you need are not available, you can build your own apks using melange. Please refer to this guide for more information.

Last updated: 2023-09-22 11:07