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