diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..05e7a92 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,54 @@ +# Dependencies +node_modules +.pnp +.pnp.js + +# Testing +coverage + +# Next.js build outputs +.next +out +build + +# Production +dist + +# Misc +.DS_Store +*.pem +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# IDE +.idea +.vscode +*.swp +*.swo + +# Git +.git +.gitignore + +# Docker +Dockerfile* +docker-compose* + +# Documentation +*.md + +# Vercel +.vercel + +# TypeScript +*.tsbuildinfo +next-env.d.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c5f0f47 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +# Multi-stage build for Next.js application +# Stage 1: Dependencies +FROM node:20-alpine AS deps +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json package-lock.json* ./ +RUN npm ci --only=production + +# Stage 2: Builder +FROM node:20-alpine AS builder +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Set environment variables for build +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production + +RUN npm run build + +# Stage 3: Runner +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +# Create a non-root user +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy built assets +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 + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e1d5099 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,35 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: pphat-nextjs-app + restart: unless-stopped + environment: + - NODE_ENV=production + env_file: + - .env + networks: + - webnet + expose: + - "3000" + + nginx: + image: nginx:alpine + container_name: pphat-nginx + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./nginx/ssl/v4.stackdev.cloud:/etc/nginx/ssl/v4.stackdev.cloud:ro + depends_on: + - app + networks: + - webnet + +networks: + webnet: + driver: bridge diff --git a/next.config.ts b/next.config.ts index 81c097a..151070a 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { + output: "standalone", images: { remotePatterns: [ { diff --git a/nginx/conf.d/v4.stackdev.cloud.conf b/nginx/conf.d/v4.stackdev.cloud.conf new file mode 100644 index 0000000..048b8cb --- /dev/null +++ b/nginx/conf.d/v4.stackdev.cloud.conf @@ -0,0 +1,75 @@ +# Upstream configuration for Next.js application +upstream nextjs_upstream { + server app:3000; + keepalive 64; +} + +# HTTP Server - Redirect to HTTPS (uncomment when SSL is configured) +# server { +# listen 80; +# listen [::]:80; +# server_name v4.stackdev.cloud; +# +# location / { +# return 301 https://$server_name$request_uri; +# } +# } + +# Main server block +server { + listen 80; + listen [::]:80; + server_name v4.stackdev.cloud; + + # Uncomment below and comment above when SSL is configured + # listen 443 ssl http2; + # listen [::]:443 ssl http2; + + # SSL Configuration (uncomment when certificates are available) + # ssl_certificate /etc/nginx/ssl/v4.stackdev.cloud/fullchain.pem; + # ssl_certificate_key /etc/nginx/ssl/v4.stackdev.cloud/privkey.pem; + # ssl_session_timeout 1d; + # ssl_session_cache shared:SSL:50m; + # ssl_session_tickets off; + # ssl_protocols TLSv1.2 TLSv1.3; + # ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + # ssl_prefer_server_ciphers off; + + # Proxy settings + location / { + proxy_pass http://nextjs_upstream; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 86400; + } + + # Static files caching + location /_next/static { + proxy_pass http://nextjs_upstream; + proxy_http_version 1.1; + proxy_set_header Host $host; + add_header Cache-Control "public, max-age=31536000, immutable"; + } + + # Public assets + location /assets { + proxy_pass http://nextjs_upstream; + proxy_http_version 1.1; + proxy_set_header Host $host; + add_header Cache-Control "public, max-age=86400"; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..0fac403 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,41 @@ +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Include additional configuration files + include /etc/nginx/conf.d/*.conf; +} diff --git a/nginx/ssl/v4.stackdev.cloud/README.md b/nginx/ssl/v4.stackdev.cloud/README.md new file mode 100644 index 0000000..dc7b189 --- /dev/null +++ b/nginx/ssl/v4.stackdev.cloud/README.md @@ -0,0 +1,20 @@ +# SSL Certificates Directory + +Place your SSL certificates here: +- `fullchain.pem` - Full certificate chain +- `privkey.pem` - Private key + +## Obtaining Certificates + +You can use Let's Encrypt with Certbot: + +```bash +certbot certonly --webroot -w /var/www/html -d v4.stackdev.cloud +``` + +Or use your preferred SSL certificate provider. + +## After Adding Certificates + +1. Uncomment the SSL configuration in `nginx/conf.d/v4.stackdev.cloud.conf` +2. Restart the nginx container: `docker compose restart nginx` diff --git a/readme.md b/readme.md index ed14ad7..a6790ee 100644 --- a/readme.md +++ b/readme.md @@ -46,6 +46,58 @@ This command will: - Create an optimized production build - Start the production server +## 🐳 Docker Deployment + +This project includes Docker configuration for containerized deployment with nginx as a reverse proxy. + +### Prerequisites for Docker + +- **Docker** (version 20.10 or higher) +- **Docker Compose** (version 2.0 or higher) + +### Quick Start with Docker + +1. Copy the environment file: +```shell +cp .env.example .env +``` + +2. Build and start the containers: +```shell +docker compose up -d --build +``` + +3. Access the application at `http://v4.stackdev.cloud` (ensure DNS is configured) + +### Docker Commands + +```shell +# Build and start containers +docker compose up -d --build + +# View logs +docker compose logs -f + +# Stop containers +docker compose down + +# Rebuild and restart +docker compose up -d --build --force-recreate +``` + +### Configuration + +- **Dockerfile**: Multi-stage build for optimized Next.js production image +- **docker-compose.yml**: Orchestrates the Next.js app and nginx services +- **nginx/conf.d/v4.stackdev.cloud.conf**: nginx configuration with `server_name v4.stackdev.cloud` + +### SSL Configuration + +To enable HTTPS: +1. Add your SSL certificates to `nginx/ssl/v4.stackdev.cloud/` +2. Uncomment the SSL configuration in `nginx/conf.d/v4.stackdev.cloud.conf` +3. Restart the nginx container + --- **💡 Quick Tip:** The development server includes TypeScript type checking and React Fast Refresh for an improved developer experience.