Containerization and Deployment
Overview
The Notes App uses Docker containerization to package and deploy the application components. This approach ensures consistent environments across development, testing, and production while simplifying deployment and scaling.
What is Containerization?
Containerization is a method of packaging applications that:
- Encapsulates applications with their dependencies and runtime environment
- Ensures consistency across different deployment environments
- Simplifies deployment by providing portable, self-contained units
- Enables scalability through orchestration tools
- Isolates applications from the host system and other containers
Docker Architecture
The Notes App uses a multi-container architecture with three main services:
- Frontend Container: React application served by Vite development server
- Backend Container: Node.js API server with Express.js
- Database Container: MongoDB database for data persistence
Docker Compose Configuration
File: docker-compose.yml
Docker Compose orchestrates multiple containers and defines their relationships.
version: '3.8'
services:
notes-front-app:
container_name: notes-front-app
build:
context: ./frontend/notes-app
dockerfile: Dockerfile
ports:
- "5173:5173"
depends_on:
- notes-api
mongo-local:
image: mongo:latest
container_name: mongo-local
ports:
- "27017:27017"
notes-api:
image: notes-api
build: ./backend/
container_name: notes-api
ports:
- "8000:8000"
depends_on:
- mongo-local
Configuration Breakdown
Version Declaration
version: '3.8'
- Compose file version: Specifies Docker Compose file format version
- Feature compatibility: Version 3.8 supports modern Docker features
Frontend Service
notes-front-app:
container_name: notes-front-app
build:
context: ./frontend/notes-app
dockerfile: Dockerfile
ports:
- "5173:5173"
depends_on:
- notes-api
Service Configuration:
container_name
: Sets a custom name for the containerbuild.context
: Specifies the build directory containing source codebuild.dockerfile
: Points to the Dockerfile for building the imageports
: Maps host port 5173 to container port 5173depends_on
: Ensures the API service starts before the frontend
Database Service
mongo-local:
image: mongo:latest
container_name: mongo-local
ports:
- "27017:27017"
Database Configuration:
image
: Uses the official MongoDB Docker imagelatest
tag: Always pulls the most recent MongoDB version- Port mapping: Exposes MongoDB on standard port 27017
Backend Service
notes-api:
image: notes-api
build: ./backend/
container_name: notes-api
ports:
- "8000:8000"
depends_on:
- mongo-local
API Configuration:
build
: Builds custom image from backend directory- Port mapping: Exposes API on port 8000
- Database dependency: Waits for MongoDB to start first
Backend Dockerfile
File: backend/Dockerfile
Defines how to build the backend API container.
# Use Node.js image as the base image
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the backend API code to the working directory
COPY . .
# Expose the port that the backend API will be running on
EXPOSE 8000
# Start the backend API
CMD ["npm", "start"]
Dockerfile Instructions Explained
Base Image
FROM node:18-alpine
FROM
: Specifies the base image to build uponnode:18-alpine
: Official Node.js version 18 on Alpine Linux- Alpine Linux: Lightweight Linux distribution, smaller image size
Working Directory
WORKDIR /app
WORKDIR
: Sets the working directory inside the container- Path consistency: All subsequent commands run from
/app
Dependency Installation
COPY package*.json ./
RUN npm install
COPY package*.json
: Copies package.json and package-lock.json- Wildcard pattern:
*
matches both package.json and package-lock.json RUN npm install
: Installs Node.js dependencies- Layer optimization: Dependencies installed before copying source code
Source Code Copy
COPY . .
COPY . .
: Copies all remaining files from build context- Efficient layering: Source changes don't invalidate dependency layer
Port Exposure
EXPOSE 8000
EXPOSE
: Documents which port the container listens on- Metadata only: Doesn't actually publish the port (done in docker-compose)
Startup Command
CMD ["npm", "start"]
CMD
: Defines the default command to run when container starts- Array syntax: Preferred format for command specification
- npm start: Runs the start script defined in package.json
Frontend Dockerfile
File: frontend/notes-app/Dockerfile
Defines how to build the frontend React container.
# Use Node.js official image as the base image
FROM node:18-alpine AS build
# Set working directory inside the container
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code to the working directory
COPY . .
# Expose the port the app will run on
EXPOSE 5173
# Command to start the development server
CMD ["npm", "run", "dev", "--", "--host"]
Frontend-Specific Configuration
Multi-stage Build Setup
FROM node:18-alpine AS build
AS build
: Names this stage for potential multi-stage builds- Development setup: Currently configured for development mode
Development Server Command
CMD ["npm", "run", "dev", "--", "--host"]
npm run dev
: Starts Vite development server--host
flag: Allows external connections to the container- Development mode: Enables hot reloading and development features
Build Tools Configuration
Vite Configuration
File: frontend/notes-app/vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})
Vite Features:
- Fast development server: Hot module replacement for quick development
- React plugin: Enables JSX transformation and React features
- Modern build tool: Optimized for modern JavaScript development
Tailwind CSS Configuration
File: frontend/notes-app/tailwind.config.js
export default {
content: ['./index.html','./src/**/*.{js,ts,jsx,tsx}',],
theme: {
extend: {
colors: {
primary: "#2B85FF",
secondary: "#EF863E"
}
},
},
plugins: [],
}
Configuration Details:
content
: Specifies files to scan for CSS classestheme.extend.colors
: Defines custom color palette- Purging: Only includes CSS for classes actually used in the application
PostCSS Configuration
File: frontend/notes-app/postcss.config.js
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
PostCSS Plugins:
tailwindcss
: Processes Tailwind CSS directivesautoprefixer
: Adds vendor prefixes for browser compatibility
Container Networking
Service Communication
Internal Network: Docker Compose creates a default network for service communication
# Backend connects to database using service name
connectionString: "mongodb://mongo-local:27017/"
Service Discovery: Containers can reach each other using service names
- Frontend → Backend:
http://notes-api:8000
- Backend → Database:
mongodb://mongo-local:27017
Port Mapping
Host to Container Mapping:
- Frontend:
localhost:5173
→notes-front-app:5173
- Backend:
localhost:8000
→notes-api:8000
- Database:
localhost:27017
→mongo-local:27017
Deployment Workflow
Development Deployment
- Build and Start:
docker-compose up --build
- Service Startup Order:
- MongoDB container starts first
- Backend API starts after database
- Frontend starts after backend API
- Access Points:
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- Database: localhost:27017
Container Lifecycle
Startup Sequence:
- Image Building: Docker builds custom images for frontend and backend
- Network Creation: Docker Compose creates internal network
- Container Startup: Containers start in dependency order
- Service Registration: Services become available on internal network
Shutdown Process:
- Graceful Shutdown:
docker-compose down
stops all containers - Network Cleanup: Removes the created network
- Container Removal: Removes stopped containers (images remain)
Benefits of This Architecture
Development Benefits
- Consistent Environment: Same setup across all developer machines
- Easy Setup: Single command starts entire application stack
- Isolation: No conflicts with other projects or system dependencies
- Hot Reloading: Development servers support live code updates
Production Benefits
- Scalability: Individual services can be scaled independently
- Portability: Containers run consistently across different environments
- Resource Efficiency: Containers share host OS kernel
- Service Isolation: Failures in one service don't affect others
Operational Benefits
- Simple Deployment: Single command deploys entire application
- Version Control: Infrastructure defined as code
- Rollback Capability: Easy to revert to previous versions
- Monitoring: Container logs and metrics available through Docker tools
Connection to Other Components
Application Architecture
- Frontend Container: Serves React application and static assets
- Backend Container: Runs Express.js API and handles business logic
- Database Container: Provides MongoDB data persistence
Configuration Management
- Environment Variables: Managed through Docker Compose
- Build Context: Source code and dependencies packaged in images
- Network Configuration: Service discovery and communication handled automatically