Hono Dockerfile for Node.js (Production, Alpine, Non-Root)

This Hono Dockerfile compiles your TypeScript app for the Node.js adapter on node:24-alpine, prunes development dependencies, and runs the compiled server as the non-root node user on port 3000. It fits Hono APIs deployed as regular containers rather than to edge platforms.

CI status
Build passing
Image size (uncompressed)
173.4 MB
Build time
9 s
Runtime image
node:24-alpine
Port
3000
Last built
2026-10-15
Dockerfile
# syntax=docker/dockerfile:1
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:24-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/package.json ./
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]
.dockerignore
.git
node_modules/
dist/
.env*
npm-debug.log*
Dockerfile
.dockerignore

Before you build

How this Dockerfile works

  1. FROM node:24-alpine AS build

    The build stage contains TypeScript and the other development tools. None of them reach the final image.

  2. COPY package.json package-lock.json ./ RUN npm ci

    Installs exact versions from the lockfile in a cached layer that's reused until the manifests change.

  3. RUN npm run build && npm prune --omit=dev

    Compiles the TypeScript source into dist, then removes devDependencies so only hono and @hono/node-server remain for runtime.

  4. ENV NODE_ENV=production

    Signals production mode to libraries that adjust logging or caching based on NODE_ENV.

  5. COPY --from=build --chown=node:node /app/dist ./dist

    Copies the compiled JavaScript next to the pruned node_modules and package.json, which holds the module type setting.

  6. USER node EXPOSE 3000 CMD ["node", "dist/index.js"]

    Starts the compiled server as the unprivileged node user, with node as the main process so stop signals arrive directly.

Build and run

terminal
docker build -t hono-app .
docker run --rm -p 3000:3000 hono-app

Build from the project root and open http://localhost:3000. Hono reads environment variables through process.env on Node, so pass configuration with -e flags.

Other Hono Dockerfile variants

The Node.js adapter is the most portable way to containerize Hono. Running Hono on Bun skips the compile step, and edge platforms like Cloudflare Workers don't use containers at all. Verified variants appear in the table below.

VariantRuntime imageImage sizeCI
Node.js server on Alpinenode:24-alpine173.4 MBPassing

Troubleshooting

Error: Cannot find module '/app/dist/index.js'
Your build writes to a different path, or the template has no build script. Check tsconfig's outDir and the scripts in package.json, then update the CMD line.
SyntaxError: Cannot use import statement outside a module
package.json is missing "type": "module" in the runtime image. Keep the COPY of package.json, which carries that setting.
The server answers inside the container but not from the host
The server binds only to localhost. Make @hono/node-server listen on all interfaces, which is its default, and publish port 3000.

FAQ

Do I need to compile TypeScript for Hono on Node?
Node needs JavaScript, so compiling ahead of time is the most reliable option for production. Running TypeScript directly through a loader adds startup overhead and dependencies.
Is Hono on Node fast enough?
For most APIs, yes. Hono's routing overhead is small, and the Node.js adapter supports streaming and standard Web APIs like Request and Response.
Can I add static files?
Yes. Use Hono's serveStatic middleware from @hono/node-server and copy your public folder into the runtime stage.