Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -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
46 changes: 46 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
35 changes: 35 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
output: "standalone",
images: {
remotePatterns: [
{
Expand Down
75 changes: 75 additions & 0 deletions nginx/conf.d/v4.stackdev.cloud.conf
Original file line number Diff line number Diff line change
@@ -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;
}
}
41 changes: 41 additions & 0 deletions nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -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;
}
20 changes: 20 additions & 0 deletions nginx/ssl/v4.stackdev.cloud/README.md
Original file line number Diff line number Diff line change
@@ -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`
52 changes: 52 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.