```dockerfile # Stage 1: Build environment FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install --omit=dev COPY . . # Assuming a build step for a typical web application # RUN npm run build
# Stage 2: Runtime environment FROM node:20-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules # COPY --from=builder /app/dist ./dist # Example for compiled assets COPY package*.json ./ COPY .env.example .env # Example .env file for local dev EXPOSE 3000 CMD ["node", "index.js"] # Adjust to your application's entry point ```
```yaml version: '3.8'
services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: DATABASE_URL: postgres://user:password@db:5432/mydb REDIS_HOST: redis SMTP_HOST: mailhog SMTP_PORT: 1025 depends_on: db: condition: service_healthy volumes: - ./wait-for-db.sh:/usr/local/bin/wait-for-db.sh entrypoint: ["/usr/local/bin/wait-for-db.sh", "db", "5432", "node", "index.js"]
db: image: postgres:15-alpine restart: always environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydb volumes: - pg_data:/var/lib/postgresql/data ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 5s timeout: 5s retries: 5
redis: image: redis:7-alpine restart: always volumes: - redis_data:/data ports: - "6379:6379"
mailhog: image: mailhog/mailhog:latest ports: - "8025:8025" # Web UI - "1025:1025" # SMTP server
volumes: pg_data: redis_data: ```
```bash #!/bin/sh # wait-for-db.sh
host="$1" port="$2" shift 2 cmd="$@"
>&2 echo "Waiting for $host:$port to be ready..."
until PGPASSWORD="password" psql -h "$host" -U "user" -d "mydb" -c '\q'; do >&2 echo "PostgreSQL is unavailable - sleeping" sleep 1 done
>&2 echo "PostgreSQL is up - executing command" exec $cmd ```
Build and Runtime Notes
To initialize the environment, build the images with docker compose build. Then, start all services in detached mode using docker compose up -d. Monitor service logs via docker compose logs -f. To halt and remove containers, networks, and persistent volumes, run docker compose down -v. For debugging, access a container shell with docker compose exec [service_name] sh.
Image Size Targets
Target application image sizes under 100MB. Employ multi-stage Dockerfiles to separate build-time dependencies from the final runtime image. Use a .dockerignore file to exclude development artifacts, test files, and version control directories from the build context. Select minimal base images, such as Alpine variants, to reduce the initial footprint.
Security Notes
Avoid embedding sensitive credentials directly in Dockerfiles or docker-compose.yml for any environment beyond isolated local development. Use .env files for local secrets, ensuring they are not committed to source control. Restrict host bind mounts to only necessary directories and avoid exposing sensitive host paths. Use Docker Compose's default network isolation or custom networks to control inter-service communication.