How to Dockerize Your Watt Application
Problem
You need to containerize your Platformatic Watt application for production deployment or to ensure consistent environments across development, staging, and production.
Solution Overview
This guide shows you how to create a multi-stage Docker build that optimizes your Watt application for production deployment. You'll create a Dockerfile that:
- Efficiently handles workspace dependencies
- Optimizes build caching
- Produces a minimal production image
- Properly configures networking for containers
Prerequisites
- Docker installed on your system
- A Platformatic Watt application ready to containerize
- Basic understanding of Docker concepts
Step 1: Configure Your Application for Containers
Ensure your watt.json
or platformatic.json
uses environment variables for hostname and port:
{
"server": {
"hostname": "{HOSTNAME}",
"port": "{PORT}"
}
}
In your development .env
file:
HOSTNAME=127.0.0.1
PORT=3042
Why this matters: Containers need to bind to all network interfaces (0.0.0.0
) to accept external connections, while development typically uses 127.0.0.1
.
Step 2: Create Your Dockerfile
Create a Dockerfile
in your project root with this multi-stage build configuration:
# syntax=docker/dockerfile:1.7-labs
# Stage 1: Build
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-alpine AS build
WORKDIR /app
# Copy all package.json files
COPY package.json ./
COPY package-lock.json ./
# Copy all package.json files from the web directories
# This uses an experimental feature to copy files from multiple directories
# and maintain the directory structure.
# https://docs.docker.com/reference/dockerfile/#copy---parents
# If this is not available in your Docker version, you can copy each package.json
# file individually. like so:
# COPY ./web/app/package.json ./web/app/package.json
COPY --parents ./web/*/package.json ./
# Install all dependencies (including dev dependencies)
RUN --mount=type=cache,target=/root/.npm npm install
# Copy the rest of the project files and run the build
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:${NODE_VERSION}-alpine AS production
WORKDIR /app
# Copy the built files from the build stage
COPY --from=build /app ./
# Install only production dependencies
RUN --mount=type=cache,target=/root/.npm npm install --omit=dev
# We must listen to all network interfaces
ENV HOSTNAME=0.0.0.0
# Set the environment variable for the port
ENV PORT=3042
# Expose the port
EXPOSE ${PORT}
# Start the application
CMD npm run start
Step 3: Build and Run Your Container
Build your Docker image:
docker build -t my-watt-app .
Run the container:
docker run -p 3042:3042 --env-file .env my-watt-app
Verification: Open http://localhost:3042
to confirm your application is running.
Understanding the Dockerfile
Multi-Stage Build Benefits
Build Stage:
- Installs all dependencies (including dev dependencies for building)
- Runs build processes that may require dev tools
- Creates optimized production assets
Production Stage:
- Copies only the built application files
- Installs only production dependencies
- Results in a smaller, more secure final image
Key Configuration Points
Network Binding:
ENV HOSTNAME=0.0.0.0
Containers must bind to all interfaces (0.0.0.0
) to accept external traffic, not just localhost.
Dependency Caching:
RUN --mount=type=cache,target=/root/.npm npm install
Caches npm downloads between builds, significantly speeding up subsequent builds.
Workspace Handling:
COPY --parents ./web/*/package.json ./
Preserves the workspace structure when copying package.json files from subdirectories.
Troubleshooting
Container exits immediately:
- Check that your
npm start
script exists in package.json - Verify your application doesn't try to connect to localhost services
Cannot reach application:
- Ensure you're using
HOSTNAME=0.0.0.0
in the container - Verify port mapping:
-p 3042:3042
Build failures:
- Check that all necessary files are copied before running build
- Verify workspace dependencies are properly handled