Tooling Lab
45 minutes
docker
nextjs
api
hands-on
Dockerized Web App + API
Build and deploy a containerized Next.js application with API routes using AI assistance.
Overview
In this lab, you'll build a complete containerized web application:
- Next.js frontend with Tailwind CSS
- API routes for backend logic
- Docker development and production configurations
- Health checks and environment management
Prerequisites
- Docker and Docker Compose installed
- Node.js 18+ (for local development)
- Basic familiarity with Next.js
Step 1: Project Setup
Step 2: Create API Route
Create a simple API endpoint:
typescript// app/api/health/route.ts import { NextResponse } from 'next/server'; export async function GET() { return NextResponse.json({ status: 'healthy', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV, }); }
Step 3: Docker Development Setup
Create the development Dockerfile:
dockerfile# Dockerfile.dev FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . EXPOSE 3000 ENV NODE_ENV=development ENV NEXT_TELEMETRY_DISABLED=1 CMD ["npm", "run", "dev"]
Create docker-compose.yml:
yaml# docker-compose.yml services: web: build: context: . dockerfile: Dockerfile.dev ports: - "3000:3000" volumes: - .:/app - /app/node_modules - /app/.next environment: - NODE_ENV=development - NEXT_TELEMETRY_DISABLED=1
Step 4: Run Development Container
Step 5: Production Dockerfile
Create an optimized production build:
dockerfile# Dockerfile.prod FROM node:22-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:22-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 RUN npm run build FROM node:22-alpine AS runner WORKDIR /app ENV NODE_ENV=production RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs USER nextjs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static EXPOSE 3000 CMD ["node", "server.js"]
Update next.config.ts for standalone output:
typescript// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'standalone', }; export default nextConfig;
Step 6: Production Compose
yaml# docker-compose.prod.yml services: web: build: context: . dockerfile: Dockerfile.prod ports: - "3000:3000" environment: - NODE_ENV=production healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"] interval: 30s timeout: 10s retries: 3 restart: always
Step 7: Test Production Build
bash# Build and run production container docker-compose -f docker-compose.prod.yml up --build # Verify health check curl http://localhost:3000/api/health
Verification Checklist
- Development container starts with hot reload
- API health endpoint returns JSON
- Production build completes without errors
- Production image is under 200MB
- Health check passes
Common Issues
| Issue | Solution |
|---|---|
| Hot reload not working | Ensure volumes are mounted correctly |
| Build fails on standalone | Add output: 'standalone' to next.config.ts |
| Permission denied | Check Docker Desktop is running |
Next Steps
- Add environment variable management with
.envfiles - Set up CI/CD pipeline for automatic builds
- Add database container with docker-compose