Contents

Docker init tutorial: Generate a Dockerfile and compose.yaml automatically

For beginners, the hardest part of writing a Dockerfile is knowing where to start. docker init detects your project's language, asks a few questions, and then generates a set of Docker files you can run right away. In this section, we test the full process with the Express project from Node.js applications and inspect the generated files line by line.

docker init is a plugin included with Docker Desktop. Linux hosts with only Docker Engine installed do not have this command, but the generated files work anywhere.

Prepare the project

The init-demo folder contains only three files and no Docker-related files yet:

init-demo/
├── package.json
├── package-lock.json
└── server.js

server.js is an Express app that listens on port 3000. Its contents are the same as in Node.js applications.

Run docker init

Run this command in the project folder:

$ 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

docker init finds package.json, marks Node as (detected), and selects it. Use the arrow keys to choose an option, then press Enter to confirm. Next, it asks these questions in order:

? What application platform does your project use? Node
? What version of Node do you want to use? 24
? Which package manager do you want to use? npm
? What command do you want to use to start the app? npm start
? What port does your server listen on? 3000

✔ Created → .dockerignore
✔ Created → Dockerfile
✔ Created → compose.yaml
✔ Created → README.Docker.md

→ Your Docker files are ready!
  Review your Docker files and tailor them to your application.
  Consult README.Docker.md for information about using the generated files.

What's next?
  Start your application by running → docker compose up --build
  Your application will be available at http://localhost:3000
QuestionAnswer in this exampleNotes
application platformNodepackage.json was detected, so press Enter
version of Node24No default; you must enter a value, or you'll see Value is required
package managernpmpackage-lock.json was detected, so npm is the default
command to start the appnpm startThe default comes from scripts.start in package.json
port3000The port the app listens on; there is no default

If you press Enter without entering a Node version, you'll see:

? What application platform does your project use? Node
X Sorry, your reply was invalid: Value is required
? What version of Node do you want to use?

If the project has an .nvmrc file or engines.node in package.json, docker init uses it as the default.

It generates four files: .dockerignore, Dockerfile, compose.yaml, and README.Docker.md.

The generated Dockerfile

Here is the content with the explanatory comments removed (the original file has English comments throughout):

# syntax=docker/dockerfile:1

ARG NODE_VERSION=24

FROM node:${NODE_VERSION}-alpine

# Use production node environment by default.
ENV NODE_ENV production

WORKDIR /usr/src/app

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

USER node

COPY . .

EXPOSE 3000

CMD npm start
SyntaxPurpose
# syntax=docker/dockerfile:1Use the latest Dockerfile syntax version (Docker downloads docker/dockerfile:1 before building)
ARG NODE_VERSION=24Store the version in a variable so you can change it with --build-arg NODE_VERSION=22
--mount=type=bind,source=package.jsonMount package.json directly during the build instead of using COPY, saving an image layer
--mount=type=cache,target=/root/.npmShare npm's download cache across builds without adding it to the image
npm ci --omit=devInstall production dependencies only
USER nodeRun as a non-root user

Overall, this follows the best practices covered in Node.js applications.

Check with docker build --check

docker build --check checks Dockerfile syntax without building the image. Run it on the generated Dockerfile:

$ docker build --check .
#0 building with "default" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 1.20kB done
#1 DONE 0.0s

#2 resolve image config for docker-image://docker.io/docker/dockerfile:1
#2 DONE 0.2s

#3 docker-image://docker.io/docker/dockerfile:1@sha256:4edf897a3ffa55b89f906fc8cc78afdb3f1834cc9c7083565e611a8a7d5fe99e
#3 resolve docker.io/docker/dockerfile:1@sha256:4edf897a3ffa55b89f906fc8cc78afdb3f1834cc9c7083565e611a8a7d5fe99e done
#3 CACHED

#4 [internal] load metadata for docker.io/library/node:24-alpine
#4 DONE 0.2s

#5 [internal] load .dockerignore
#5 transferring context: 663B done
#5 DONE 0.0s
Check complete, 2 warnings have been found!

WARNING: LegacyKeyValueFormat - https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
"ENV key=value" should be used instead of legacy "ENV key value" format
Dockerfile:14
--------------------
  12 |
  13 |     # Use production node environment by default.
  14 | >>> ENV NODE_ENV production
  15 |
  16 |
--------------------

WARNING: JSONArgsRecommended - https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
JSON arguments recommended for CMD to prevent unintended behavior related to OS signals
Dockerfile:38
--------------------
  36 |
  37 |     # Run the application.
  38 | >>> CMD npm start
  39 |
--------------------

The template has two warnings:

  1. LegacyKeyValueFormat: ENV NODE_ENV production uses the old syntax. Change it to ENV NODE_ENV=production.
  2. JSONArgsRecommended: CMD npm start uses shell form. JSON array form is recommended to avoid possible issues forwarding stop signals. See Dockerfile instructions for details.

Change these two lines:

ENV NODE_ENV=production
CMD ["node", "server.js"]

The CMD also runs node directly, removing an npm process. See the test in Node.js applications for why. Adding docker build --check to your CI workflow is a good way to catch syntax issues before building.

The generated compose.yaml

After removing the comments, only a few lines remain:

services:
  server:
    build:
      context: .
    environment:
      NODE_ENV: production
    ports:
      - 3000:3000

The lower half of the original file is a large commented-out PostgreSQL example. If you uncomment it, keep two issues in mind:

#   db:
#     image: postgres
#     ...
#     volumes:
#       - db-data:/var/lib/postgresql/data
#     ...
#     healthcheck:
#       test: [ "CMD", "pg_isready" ]
  • image: postgres does not specify a version, so it currently pulls version 18. Version 18 requires the volume to be mounted at /var/lib/postgresql. Mounting it at /var/lib/postgresql/data will cause startup to fail. See PostgreSQL.
  • pg_isready does not include -h 127.0.0.1, so the database may be reported as healthy before initialization is complete.

.dockerignore and README.Docker.md

The generated .dockerignore excludes common files that are not needed, including **/node_modules, **/.env, **/.git, **/Dockerfile*, and **/compose.y*ml. It is more complete than the version you wrote by hand earlier in this tutorial, so you can use it as-is.

README.Docker.md is a brief guide in English. It explains how to start the app with docker compose up --build and notes building for different CPU architectures (see Multi-platform images).

Start the app

Follow the prompt and run docker compose up --build. Here, add -d to run it in the background, then check the results:

$ docker compose up -d --build

[+] Building 15.1s (13/13) FINISHED
 => [internal] load local bake definitions                                                                                                       0.0s
 => => reading from stdin 486B                                                                                                                   0.0s
 => [internal] load build definition from Dockerfile                                                                                             0.0s
 => => transferring dockerfile: 1.20kB                                                                                                           0.0s
 => resolve image config for docker-image://docker.io/docker/dockerfile:1                                                                        1.1s
 => docker-image://docker.io/docker/dockerfile:1@sha256:4edf897a3ffa55b89f906fc8cc78afdb3f1834cc9c7083565e611a8a7d5fe99e                         3.9s
 => => resolve docker.io/docker/dockerfile:1@sha256:4edf897a3ffa55b89f906fc8cc78afdb3f1834cc9c7083565e611a8a7d5fe99e                             0.0s
 => => sha256:9d2b4f57f9e20f33279375ce25c6ff14f30f64270ed5c7b613b2b30256754f62 14.28MB / 14.28MB                                                 3.5s
 => => extracting sha256:9d2b4f57f9e20f33279375ce25c6ff14f30f64270ed5c7b613b2b30256754f62                                                        0.4s
 => [internal] load metadata for docker.io/library/node:24-alpine                                                                                0.9s
 => [internal] load .dockerignore                                                                                                                0.0s
 => => transferring context: 663B                                                                                                                0.0s
 => [stage-0 1/4] FROM docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401c70e0e762c5b53302f905ec1c1                  3.2s
 => => resolve docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401c70e0e762c5b53302f905ec1c1                          0.0s
 => => extracting sha256:e2de96513ba9eb53b431787ec8a65cdde380ac4772a3e4c4b714dcfde2a102b5                                                        0.2s
 => => sha256:02f40d53b06f6b91fec0cd6a6719656da99270bff8ee54ed8dcdf4c157763fc1 1.26MB / 1.26MB                                                   0.8s
 => => sha256:7ce7dcbafb7f750cf0c6be3c30e34372b06dd1182fa67f6ae30782c8150c5444 444B / 444B                                                       0.2s
 => => sha256:3c504d5a797be28f9de43147d49cd8ecb2eea46dfaf7beb8715db695d5f0a7f0 56.52MB / 56.52MB                                                 1.1s
 => => extracting sha256:3c504d5a797be28f9de43147d49cd8ecb2eea46dfaf7beb8715db695d5f0a7f0                                                        1.9s
 => => extracting sha256:02f40d53b06f6b91fec0cd6a6719656da99270bff8ee54ed8dcdf4c157763fc1                                                        0.1s
 => => extracting sha256:7ce7dcbafb7f750cf0c6be3c30e34372b06dd1182fa67f6ae30782c8150c5444                                                        0.0s
 => [internal] load build context                                                                                                                0.0s
 => => transferring context: 31.23kB                                                                                                             0.0s
 => [stage-0 2/4] WORKDIR /usr/src/app                                                                                                           0.1s
 => [stage-0 3/4] RUN --mount=type=bind,source=package.json,target=package.json     --mount=type=bind,source=package-lock.json,target=package-l  2.6s
 => [stage-0 4/4] COPY . .                                                                                                                       0.0s
 => exporting to image                                                                                                                           2.7s
 => => exporting layers                                                                                                                          0.3s
 => => exporting manifest sha256:1278071dc9c5842ea834d9abc8c01262b999118ef6903ed4998b8cc8ccc5aaba                                                0.0s
 => => exporting config sha256:879a23594d9f079707b7b35c022fb427cebaaa18db9701eb78b1fbaf82efed06                                                  0.0s
 => => exporting attestation manifest sha256:012342031ad01079856bb6ebf257f096c2d8f9ff12d448ed86a58a645d64bb5d                                    0.0s
 => => exporting manifest list sha256:19aee69cf5d970c251d176f2f533bb918f9e72edf675b109b569ae2ff25cb4f3                                           0.0s
 => => naming to docker.io/library/init-demo-server:latest                                                                                       0.0s
 => => unpacking to docker.io/library/init-demo-server:latest                                                                                    2.3s
 => resolving provenance for metadata file                                                                                                       0.0s
[+] up 3/3
 ✔ Image init-demo-server       Built                                                                                                            15.2s
 ✔ Network init-demo_default    Created                                                                                                           0.0s
 ✔ Container init-demo-server-1 Started                                                                                                           0.4s

$ curl -s http://localhost:3000
{"message":"Hello from Express","node":"v24.21.0"}

$ docker compose exec server ps
PID   USER     TIME  COMMAND
    1 node      0:00 npm start
   18 node      0:00 {MainThread} node server.js
   25 node      0:00 ps

$ time docker compose stop
 Container init-demo-server-1 Stopping
 Container init-demo-server-1 Stopped
real	0m 0.21s
user	0m 0.04s
sys	0m 0.02s

$ docker compose logs server
server-1  |
server-1  | > express-app@1.0.0 start
server-1  | > node server.js
server-1  |
server-1  | Listening on port 3000
server-1  | SIGTERM received, closing server
server-1  | npm notice
server-1  | npm notice New major version of npm available! 11.19.0 -> 12.2.0
server-1  | npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.0
server-1  | npm notice To update run: npm install -g npm@12.2.0
server-1  | npm notice
  • The first build step downloads docker/dockerfile:1, which is caused by the # syntax= line.
  • The service is named server, and the app responds successfully.
  • ps shows that PID 1 is npm start and node is its child process. docker compose stop finishes in 0.21 seconds, and npm forwards the stop signal to node.

When to use docker init

  • A new project, and you're unfamiliar with Dockerfiles: Generate a starting point, then understand and edit it line by line alongside this tutorial.
  • A project that already has a Dockerfile: Generate another copy in a separate folder and use it as a reference (for example, for cache mounts).
  • What it won't do: It won't handle database migrations, environment variables, HTTPS, or the details of multi-stage builds for you. The generated files are only a starting point.

Recap

  • docker init detects your project's language, asks a few questions, and generates a Dockerfile, compose.yaml, .dockerignore, and README.Docker.md.
  • The Node version and port have no defaults; you must enter them.
  • The generated Dockerfile uses BuildKit bind and cache mounts. The approach is good, but it still triggers warnings about old syntax.
  • Use docker build --check to check any Dockerfile.
  • The commented-out PostgreSQL example in the template needs changes to work with postgres:18.

Exercises

Exercise 1: Run docker build --check . on a Dockerfile you've written (for example, the one in Dockerfile basics) and see whether it produces warnings.

Hint

If there are no issues, it displays Check complete, no warnings found.. The most common warnings are JSONArgsRecommended (shell form used for CMD or ENTRYPOINT) and LegacyKeyValueFormat (ENV used without =).

Exercise 2: Change the Node version in the generated Dockerfile to 22 without editing the file.

Answer

NODE_VERSION is an ARG, so pass it in at build time:

docker build --build-arg NODE_VERSION=22 -t init-demo:node22 .

In Compose, set it under build.args:

services:
  server:
    build:
      context: .
      args:
        NODE_VERSION: "22"

Exercise 3: Follow the instructions in this chapter to fix the ENV and CMD lines. Then run docker build --check . again and confirm the warnings are gone.

FAQ

Can I use docker init with Docker Engine on Linux?

docker init is a CLI plugin included with Docker Desktop (available on Windows, macOS, and Linux). Linux hosts with only Docker Engine installed do not have this command by default, so running it displays docker: unknown command: docker init.

Can I run docker init in a project that already has a Dockerfile?

Yes, but it will ask whether to overwrite existing files. Commit your work to Git first, or try it in another folder so you can compare the generated files before deciding whether to use them.

Which languages does docker init support?

In v1.4.0, the menu includes Node, Go, Python, Rust, ASP.NET Core, PHP with Apache, Java, and the general-purpose Other option. It detects files in your project (such as package.json) and selects defaults automatically.

Can I use the generated files in production as-is?

They make a good starting point, but review them yourself. For example, the template tested in this chapter uses the outdated ENV key value syntax and shell-form CMD, which docker build --check warns about. The commented-out PostgreSQL example in the template also does not work with postgres:18.

Updated October 13, 2026