Skip to main content

Production Deployment Guide

Complete guide for deploying this application to production using Docker, Nginx, and SSL.


Table of Contents​

  1. Prerequisites
  2. Server Requirements
  3. Security Hardening
  4. Deployment Options
  5. Nginx Configuration
  6. SSL Setup
  7. SEO & Indexing
  8. Maintenance

Prerequisites​

Required:

  • Linux server (Ubuntu 22.04/24.04 recommended)
  • Docker and Docker Compose installed
  • Domain name (e.g., yourstore.com)
  • SSH access to server

For Self-Hosting:

  • Router access for port forwarding
  • Static local IP address

For Cloud VPS:

  • VPS provider account (DigitalOcean, Hetzner, AWS, Linode, etc.)

Server Requirements​

Compute Resources​

Traffic TierMonthly VisitsCPU (vCores)RAM (Memory)Storage (SSD/NVMe)
Low< 10,0002 vCPUs4 GB40 GB
Medium10k - 100k4 vCPUs8 - 16 GB80 GB
High100k - 500k+8+ vCPUs32 GB+160 GB+

Storage Requirements​

Storage needs based on product catalog size:

Calculation Assumptions:

  • Average Images per Product: 3.5
  • Average Image Size: 2MB (high-res) + Next.js optimized cache
  • Database growth includes metadata, users, and order history
ScaleProductsEst. ImagesDatabase (Postgres)Media (Uploads)Total Recommended
Startup2,000~7,0001 GB~15 GB25 GB SSD
Growth10,000~35,0005 GB~75 GB100 GB NVMe
Scale50,000~175,00020 GB~375 GB500 GB NVMe
Enterprise100,000+~350,000+50 GB+~750 GB+1 TB+ NVMe

Security Hardening​

Hardened Dockerfile​

Use this multi-stage build that removes dangerous network tools in production:

# ==========================================
# 1. Base Stage
# ==========================================
FROM node:22-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm install --force

# ==========================================
# 2. Development Stage
# ==========================================
FROM base AS dev
COPY . .
# Security: Remove tools botnets use
RUN rm -rf /usr/bin/wget /usr/bin/curl /usr/bin/nc
EXPOSE 3000
CMD ["npm", "run", "dev"]

# ==========================================
# 3. Production Stage
# ==========================================
FROM base AS prod
WORKDIR /app
ENV NEXT_PUBLIC_NODE_ENV=production

# Copy application files
COPY . .

# Setup Entrypoint
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Build inside the image
RUN npx prisma generate
RUN npm run build

# --- SECURITY PATCH ---
# Remove dangerous tools to prevent malware downloads
RUN rm -rf /usr/bin/wget /usr/bin/curl /usr/bin/nc /usr/bin/netcat

EXPOSE 3000
ENTRYPOINT ["docker-entrypoint.sh"]

Secure docker-compose.yml​

Ensure services are NOT exposed to the public internet. Bind to localhost only:

services:
web:
# ...
ports:
- "127.0.0.1:3000:3000" # Only accessible by Nginx

db:
# ...
ports:
- "127.0.0.1:5432:5432" # Block external attacks

Deployment Options​

Pre-Build Cache Transfer (Optional Performance Boost)​

Skip the 5-10 minute build process by transferring a pre-built cache.

On Local Machine:

# Build Docker cache locally
docker compose up --build

# Create compressed archive
tar -czf docker-cache.tar.gz .docker-cache

# Check size (typically 200-500MB)
ls -lh docker-cache.tar.gz

# Transfer to VPS
scp docker-cache.tar.gz user@your-vps-ip:/path/to/project/

# Alternative: Use rsync for incremental transfers
rsync -avz --progress docker-cache.tar.gz user@your-vps-ip:/path/to/project/

On VPS:

# Navigate to project directory
cd /path/to/project

# Extract cache
tar -xzf docker-cache.tar.gz

# Verify extraction
ls -la .docker-cache/
# Should show: node_modules/ and .next/

# Clean up archive (optional)
rm docker-cache.tar.gz

# Start containers (uses pre-built cache)
docker compose up -d

Cache Management Commands​

Update with Full Cache Transfer (major updates):

# Local:
docker compose up --build
tar -czf docker-cache.tar.gz .docker-cache
scp docker-cache.tar.gz user@vps:/path/to/project/

# VPS:
cd /path/to/project
docker compose down
tar -xzf docker-cache.tar.gz
docker compose up -d

Incremental Sync (minor updates):

# Local:
rsync -avz --progress .docker-cache/ user@vps:/path/to/project/.docker-cache/

# VPS:
docker compose restart app

Clear Cache (force rebuild):

docker compose down
rm -rf .docker-cache
docker compose up --build -d # Takes 5-10 minutes

Check Cache Status:

du -sh .docker-cache/node_modules
du -sh .docker-cache/.next
du -sh .docker-cache # Total size

Option 2: Self-Hosting (Home Server)​

Network Setup​

Requirements:

  • Static local IP for your server (e.g., 192.168.1.100)
  • Router admin access (usually 192.168.1.1 or 192.168.1.254)
  • Dynamic DNS service (e.g., DuckDNS)

Port Forwarding Configuration:

  1. Login to your router admin panel
  2. Navigate to Port Forwarding section
  3. Create two forwarding rules to your server's local IP:
    • Port 80 (HTTP) → Port 80
    • Port 443 (HTTPS) → Port 443

Dynamic DNS Setup (DuckDNS Example):

  1. Register at duckdns.org
  2. Create subdomain (e.g., yourstore.duckdns.org)
  3. Follow their guide to auto-update your public IP

Nginx Configuration​

Installation​

sudo apt update
sudo apt install nginx
sudo ufw allow 'Nginx Full' # Open firewall

Verify installation by visiting your server IP - you should see "Welcome to nginx!"

Main Configuration​

Edit the main config file:

sudo nano /etc/nginx/nginx.conf

Complete nginx.conf:

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

events {
worker_connections 10240;
use epoll;
multi_accept on;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Logging Optimization
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 buffer=32k flush=3s;

# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
client_max_body_size 100G;

# Timeouts
keepalive_timeout 300;
client_body_timeout 300;
client_header_timeout 300;

# Rate Limiting (100 req/sec, burst 200)
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=100r/s;

# Upstream to Docker
upstream backend_app {
server 127.0.0.1:3000;
keepalive 64;
}

# HTTP -> HTTPS Redirect
server {
listen 80;
server_name your-domain.com www.your-domain.com;

# Allow Certbot validation
location /.well-known/acme-challenge/ {
root /var/www/html;
}

# Redirect all other traffic to HTTPS
location / {
return 301 https://$host$request_uri;
}
}

# HTTPS Server (uncomment AFTER running Certbot)
# server {
# listen 443 ssl;
# http2 on;
# server_name your-domain.com www.your-domain.com;
#
# ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# include /etc/letsencrypt/options-ssl-nginx.conf;
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
#
# # Main application
# location / {
# limit_req zone=mylimit burst=200 nodelay;
# proxy_pass http://backend_app;
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# proxy_set_header Host $http_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_cache_bypass $http_upgrade;
# }
#
# # SSE / Live Streams (No Buffering)
# location /api/admin/live {
# proxy_buffering off;
# proxy_cache off;
# gzip off;
# proxy_pass http://backend_app;
# proxy_http_version 1.1;
# proxy_set_header Connection "";
# proxy_set_header Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# }
# }

include /etc/nginx/conf.d/*.conf;
}

Test and Apply:

sudo nginx -t                # Check for syntax errors
sudo systemctl reload nginx # Apply changes

SSL Setup​

Domain Configuration​

For Custom Domains:

  1. Go to your domain registrar (e.g., Libyan Spider, Namecheap, GoDaddy)
  2. Navigate to DNS Management / Zone Editor
  3. Add A Records:
    • Host: @ → Value: YOUR_VPS_IP
    • Host: www → Value: YOUR_VPS_IP
  4. Wait for DNS propagation (5-60 minutes)
  5. Verify: ping your-domain.com

For DuckDNS (Self-Hosting):

Follow the DuckDNS setup guide to link your subdomain to your public IP.

SSL Certificate Installation​

# Install Certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx

# For regular domains:
sudo certbot certonly --nginx -d your-domain.com -d www.your-domain.com

# For DuckDNS:
sudo certbot certonly --nginx -d yourstore.duckdns.org

After successful certificate generation:

  1. Uncomment the HTTPS server block in /etc/nginx/nginx.conf
  2. Replace your-domain.com with your actual domain
  3. Restart Nginx:
sudo nginx -t
sudo systemctl restart nginx

Your site is now live at https://your-domain.com 🎉

You can then implement other things such as default fallback page when the server is down and so on.


SEO & Indexing​

Dynamic Sitemap Generation​

You'll have this sitemap generator: src/app/sitemap.js to automatically list all products every 24 hours, you can edit it if you want.

Google Search Console Setup​

  1. Go to Google Search Console
  2. Click Add Property → Select Domain
  3. Copy the google-site-verification code
  4. Add TXT Record in your DNS:
    • Host: @
    • Value: google-site-verification=XXXXX
  5. Return to Search Console and click Verify
  6. Navigate to Sitemaps → Submit https://your-domain.com/sitemap.xml

Maintenance​

Common Commands​

ActionCommand
Deploy Updatesgit pull && docker compose up --build -d
View Application Logsdocker compose logs -f web
View Nginx Logssudo tail -f /var/log/nginx/access.log
Restart Nginxsudo systemctl restart nginx
Check Nginx Statussudo systemctl status nginx
Test Nginx Configsudo nginx -t
Renew SSLsudo certbot renew (auto-renews normally)
Stop Containersdocker compose down
Restart Containersdocker compose restart

Monitoring​

Check container health:

docker compose ps

View resource usage:

docker stats

Database backup:

docker compose exec db pg_dump -U your_user your_database > backup.sql

Alternative Deployment Options​

This application can also be deployed using:

  • Asura Hosting - Specialized Node.js hosting
  • Ngrok - Quick tunneling for development/testing
  • Cloudflare Tunnel - Secure tunneling without port forwarding
  • Railway.app - Platform-as-a-Service deployment
  • Vercel - Serverless Next.js deployment (requires database adjustments)

Refer to their respective documentation for setup instructions.


Troubleshooting​

Port already in use:

sudo lsof -i :80
sudo lsof -i :443
# Kill the process if needed

SSL certificate renewal fails:

sudo certbot renew --dry-run
# Check DNS and firewall settings

Application not accessible:

# Check if Docker containers are running
docker compose ps

# Check if Nginx is running
sudo systemctl status nginx

# Check firewall
sudo ufw status

Database connection errors:

# Check database container
docker compose logs db

# Verify DATABASE_URL in .env

Last updated on February 16, 2026, by Ayman.