Table of Contents
Introduction
Historically, Docker has been a popular choice for containerizing applications. In the cloud native era, there is another popular player for performing app image build and getting lots of attention in devops community. It's quite easy to use cloud native buildpacks to containerize the app. There is no specific file or config required in most of the cases. Cloud native buildpacks supports all the popular coding languages and work smoothly to identify the language based on the source-code to containerize app image.
In this blog we would go through the steps to containerize the Next.js based webapp using simple steps. For building Next.js app image with Docker build, please have a look into another blog - How to containerize Next.js app with docker build.
Initial setup
We can build a Next.js based webapp with npx create-next-app@latest
. We can extend the web app with more functionalities and API's.
Based of the usecase, developer can build a standalone output for the Next.js app. Related feature in Next.js for generating standalone output can be enabled in the next.config.js
.
module.exports = {
output: 'standalone',
}
Above config will create a folder at .next/standalone
which can then be deployed on its own without installing node_modules.
NOTE: Though standalone output feature is good to have for containerizing app with docker build, but for buildpacks, it's an optional step.
Introduce Pack CLI
The simplest way to use buildpacks is by installing Pack CLI in the dev environment. There is no need to install docker or any other dependency except Pack CLI for image build.
NOTE: We might need docker to perform a test-run for the built image with docker run
.
$ pack
CLI for building apps using Cloud Native Buildpacks
Usage:
pack [command]
Available Commands:
build Generate app image from source code
builder Interact with builders
buildpack Interact with buildpacks
extension Interact with extensions
config Interact with your local pack config file
inspect Show information about a built app image
stack Interact with stacks
rebase Rebase app image with latest run image
sbom Interact with SBoM
completion Outputs completion script location
report Display useful information for reporting an issue
version Show current 'pack' version
help Help about any command
Pack Build requires an image name, which will be generated from the source code. Build defaults to the current directory, but you can use --path
to specify another source code directory. Build requires a builder
, which can either be provided directly to build using --builder
, or can be set using the set-default-builder
command. To specify build time environment variables, we can pass via --env
or can specify path for env-file with --env-file
. All the other options can be checked with --help
for pack build sub-command.
NOTE1: A builder is an image that contains all the components necessary to execute a build.
NOTE2 A buildpack is a set of executables that inspects your app source code and creates a plan to build and run your application.
Detailed doc for Pack build can be found at : https://buildpacks.io/docs/app-developer-guide/build-an-app
Containerize Next.js app
Containerizing Next.js app to image is as simple as running the pack build
command with certain options.
--name
: The name and the tag of the app image.--builder
: The builder to use for image build.--buildpack
: The buildpack for nodejs.
We can run below command to start the image creation process.
pack build nextapp:1.0 \
--path /home/user1/nextapp \
--buildpack paketo-buildpacks/nodejs \
--builder paketobuildpacks/builder:full
Considerations for Next.js app
-
By default, buildpacks look into package.json for the build script and runs the command during build. So, to ensure that the build gets the env params from buildpack for production, after the
next build
we can either specify the build env param or we can add 2nd build script as"build:prod": "set NODE_ENV=production & next build"
. -
In case we are using Next.js standalone output generation during build for getting the app deploy resources, we need to change the start script for app as
"start": "node .next/standalone/server.js"
. If we are not using standalone output, then we do not need any change as start script can be as"start": "next start"
.
$ cat package.json
{
"name": "nextapp",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"build:prod": "set NODE_ENV=production & next build",
"start": "node .next/standalone/server.js",
"lint": "next lint"
},
...
}
NOTE: Above package.json considers start script for Next.js app with standalone output.
Build process
With above considerations, we can run the pack build
command and track the image generation. The build process downloads the builder image and corresponding buildpacks image in first attempt. The process identifies the supporting buildpacks needed for the app based on the source-code and configs defined. Also adds environment variables, which can be overridden with CLI.
$ pack build nextapp:1.0 \
--path /home/user1/nextapp \
--buildpack paketo-buildpacks/nodejs \
--builder paketobuildpacks/builder:full
full: Pulling from paketobuildpacks/builder
Digest: sha256:11068838eeb5cb3a34bb34f56de5e9a5c40c1eb31995046e3076ebde7b499c9f
Status: Image is up to date for paketobuildpacks/builder:full
full-cnb: Pulling from paketobuildpacks/run
Digest: sha256:5373ba74412664ac24f132e29b94808467d8522e5f79557d5aea72571c15e7fe
Status: Image is up to date for paketobuildpacks/run:full-cnb
===> ANALYZING
Image with name "nextapp:1.0" not found
===> DETECTING
5 of 11 buildpacks participating
paketo-buildpacks/ca-certificates 3.6.2
paketo-buildpacks/node-engine 1.6.0
paketo-buildpacks/npm-install 1.1.4
paketo-buildpacks/node-run-script 1.0.10
paketo-buildpacks/npm-start 1.0.11
===> RESTORING
Restoring metadata for "paketo-buildpacks/npm-install:npm-cache" from cache
Restoring data for "paketo-buildpacks/npm-install:npm-cache" from cache
Restoring data for SBOM from cache
===> BUILDING
Paketo Buildpack for CA Certificates 3.6.2
https://github.com/paketo-buildpacks/ca-certificates
Launch Helper: Contributing to layer
Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
Paketo Buildpack for Node Engine 1.6.0
Resolving Node Engine version
Candidate version sources (in priority order):
-> ""
<unknown> -> ""
Selected Node Engine version (using ): 18.16.1
Executing build process
Installing Node Engine 18.16.1
Completed in 3.172s
Generating SBOM for /layers/paketo-buildpacks_node-engine/node
Completed in 0s
Configuring build environment
NODE_ENV -> "production"
NODE_HOME -> "/layers/paketo-buildpacks_node-engine/node"
NODE_OPTIONS -> "--use-openssl-ca"
NODE_VERBOSE -> "false"
Configuring launch environment
NODE_ENV -> "production"
NODE_HOME -> "/layers/paketo-buildpacks_node-engine/node"
NODE_OPTIONS -> "--use-openssl-ca"
NODE_VERBOSE -> "false"
Writing exec.d/0-optimize-memory
Calculates available memory based on container limits at launch time.
Made available in the MEMORY_AVAILABLE environment variable.
Paketo Buildpack for NPM Install 1.1.4
Resolving installation process
Process inputs:
node_modules -> "Not found"
npm-cache -> "Not found"
package-lock.json -> "Found"
Selected NPM build process: 'npm ci'
Executing build environment install process
Running 'npm ci --unsafe-perm --cache /layers/paketo-buildpacks_npm-install/npm-cache'
added 417 packages, and audited 418 packages in 10s
140 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Completed in 13.114s
Configuring build environment
NODE_ENV -> "development"
PATH -> "$PATH:/layers/paketo-buildpacks_npm-install/build-modules/node_modules/.bin"
Generating SBOM for /layers/paketo-buildpacks_npm-install/build-modules
Completed in 2.025s
Executing launch environment install process
Running 'npm prune'
up to date, audited 418 packages in 1s
140 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Completed in 1.617s
Configuring launch environment
NODE_PROJECT_PATH -> "/workspace"
NPM_CONFIG_LOGLEVEL -> "error"
PATH -> "$PATH:/layers/paketo-buildpacks_npm-install/launch-modules/node_modules/.bin"
Generating SBOM for /layers/paketo-buildpacks_npm-install/launch-modules
Completed in 1.875s
Paketo Buildpack for Node Run Script 1.0.10
Executing build process
Running 'npm run build'
> [email protected] build
> next build
- warn You are using a non-standard "NODE_ENV" value in your environment. This creates inconsistencies in the project and is strongly advised against. Read more: https://nextjs.org/docs/messages/non-standard-node-env
Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry
- info Creating an optimized production build...
- info Compiled successfully
- info Linting and checking validity of types...
- info Collecting page data...
- info Generating static pages (0/5)
- info Generating static pages (1/5)
2023-08-02 07:53:46 [Env: production | about] info: About page called!
2023-08-02 07:53:46 [Env: production | home] info: Home page called!
2023-08-02 07:53:46 [Env: production | about] info: About page called!
- info Generating static pages (2/5)
- info Generating static pages (3/5)
2023-08-02 07:53:46 [Env: production | home] info: Home page called!
- info Generating static pages (5/5)
- info Finalizing page optimization...
Route (app) Size First Load JS
┌ ○ / 137 B 78.3 kB
├ ○ /about 409 B 88.7 kB
└ λ /api/hello 0 B 0 B
+ First Load JS shared by all 78.1 kB
├ chunks/487-a6e261780a8e8119.js 25.7 kB
├ chunks/9c58a40f-267e5b531cccdcfa.js 50.5 kB
├ chunks/main-app-a1fd79d1fa4b0287.js 215 B
└ chunks/webpack-7e6dae598cf9ea88.js 1.71 kB
Route (pages) Size First Load JS
─ ○ /404 183 B 75.7 kB
+ First Load JS shared by all 75.5 kB
├ chunks/framework-7d3832b2720a7694.js 45 kB
├ chunks/main-51a549b6bdec5f8d.js 28.6 kB
├ chunks/pages/_app-e266fe44633eda69.js 194 B
└ chunks/webpack-7e6dae598cf9ea88.js 1.71 kB
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
npm notice
npm notice New minor version of npm available! 9.5.1 -> 9.8.1
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v9.8.1>
npm notice Run `npm install -g [email protected]` to update!
npm notice
Completed in 20.079s
Paketo Buildpack for NPM Start 1.0.11
Assigning launch processes:
web (default): sh /workspace/start.sh
===> EXPORTING
Adding layer 'paketo-buildpacks/ca-certificates:helper'
Adding layer 'paketo-buildpacks/node-engine:node'
Adding layer 'paketo-buildpacks/npm-install:launch-modules'
Adding layer 'buildpacksio/lifecycle:launch.sbom'
Adding 1/1 app layer(s)
Adding layer 'buildpacksio/lifecycle:launcher'
Adding layer 'buildpacksio/lifecycle:config'
Adding layer 'buildpacksio/lifecycle:process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Setting default process type 'web'
Saving nextapp:1.0...
*** Images (7ecb7d61bb14):
nextapp:1.0
Reusing cache layer 'paketo-buildpacks/node-engine:node'
Adding cache layer 'paketo-buildpacks/npm-install:build-modules'
Adding cache layer 'paketo-buildpacks/npm-install:npm-cache'
Adding cache layer 'buildpacksio/lifecycle:cache.sbom'
Successfully built image nextapp:1.0
Conclusion
We can verify that the image is created with docker images
and can be tested with docker run
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
paketobuildpacks/run full-cnb 7877b1bacecb 4 weeks ago 687MB
nextapp 1.0 7ecb7d61bb14 43 years ago 1.31GB
paketobuildpacks/builder full 11bf29c66db6 43 years ago 2.11GB
$ docker run --rm --name app -p 3000:3000 nextapp:1.0
Listening on port 3000 url: http://2371e8d541f6:3000
2023-08-02 07:54:27 [Env: production | hello-route] info: Request headers: {}
Hope this blog will help my friends to get the steps to build app image with buildpacks in quick and easy steps. 🙂