Challenge,Β Medium, Β onΒ  Containers

You are tasked with optimizing the container image for a Next.js application. The source code for the application is located in the ~/app directory, along with an initial Dockerfile. However, the current setup does not adhere to containerization best practices. Your goal is to improve the Dockerfile and create a production-ready container image.

Build a Docker image named registry.iximiuz.com/app:v1.0.0 that meets the following requirements:

  1. Base Image:
  • Use an optimal Node.js base image.
  • Ensure the Node.js version is the active LTS version.
  1. Security:
  • The image must run as a non-root user.
  • Include only necessary packages and files.
  1. Performance:
  • The final image size must not exceed 300 MB.
  1. Runtime:
  • The container should not depend on npm at runtime.
  • The application must:
    • Listen on 0.0.0.0:3000.
    • Include all assets from the public folder.
    • Serve APIs and static files correctly.
  1. Testing:
  • Verify the application by running a container and checking the main page and the API handlers responses.

The final solution will be validated using automated checks. You can access dynamic hints from the checks by clicking the task box below.

Good luck! πŸš€

Hint 1 πŸ’‘

If you're new to Docker, you may want to solve this simpler challenge first: Build and Publish a Container Image With Docker

Hint 2 πŸ’‘

One of the tricky parts when containerizing a Node.js application is choosing the right base image. You can find many variants of Node.js base images, but not all of them are suitable for production use. If you don't know what option is best for your case, check out this material on how to choose a Node.js container image for your application.

Node.js base image choices: the Docker "Official" images, Bitnami, GoogleContainerTools distroless, and Chainguard.
Hint 3 πŸ’‘

Modern Node.js applications tend to depend on lots of packages. Some of these packages (e.g., linters, formatters, etc.) are needed only during the development time, while others (e.g., the framework's runtime, libraries, etc.) are needed at production time, too.

The node_modules folder containing both development and production dependencies is usually quite large, and it should never be included in the production container image.

Luckily, frameworks like Next.js (src) or Nuxt (src) have a way to produce a standalone build of the application, that doesn't require the node_modules folder at runtime at all (usually via npm run build).

However, to build a standalone bundle, you need to install all dependencies first, so if you're using a single-stage build, you'll end up with a large image anyway:

Single-stage Node.js image build: The resulting runtime image contains lots of unwanted dev/build-time dependencies.

To separate the build- and runtime dependencies, a multi-stage Docker build can be used:

Multi-stage Node.js image build: The build stage installs all dependencies, while the runtime stage only includes the production dependencies.

If you're not familiar with this technique, check out this tutorial on how to produce smaller container images with multi-stage builds.

Hint 4 πŸ’‘

Wondering why the image should not depend on npm at runtime? First of all, it's an extra dependency that you probably don't even want to have in the production image because it increases the image size and the attack surface. Second, it often messes with the graceful termination of the container:

Dockerfile ENTRYPOINT and CMD instructions: The "npm start" command is not a good choice for production images.

Check out this challenge for more details: Ensure a Graceful Termination for a Containerized Node.js Application

Level up your Server Side game β€” Join 9,000 engineers who receive insightful learning materials straight to their inbox