Overview
ICL Cotizaciones is a Next.js 16 application with a SQLite database. This guide covers deployment to production environments.
Architecture
The application consists of:
- Web Server: Next.js 16 App Router on Node.js
- Database: SQLite with better-sqlite3 (WAL mode)
- Session Store: Encrypted HTTP-only cookies (iron-session)
- Static Assets: Served by Next.js
Prerequisites
| Requirement | Version | Notes |
|---|
| Node.js | 20+ | Required for Next.js 16 |
| npm | Latest | Included with Node.js |
| SQLite3 | 3.x | For database CLI tools |
Environment Configuration
The application currently has hardcoded secrets in source code. These must be moved to environment variables before deploying to production.
Required Environment Variables
Create a .env.production file with the following variables:
# Session encryption key (minimum 32 characters)
SESSION_PASSWORD=your-secure-random-password-at-least-32-chars
# Node.js environment
NODE_ENV=production
# Server port (default: 3000)
PORT=3000
# Database path (optional, defaults to {cwd}/data/icl.db)
DATABASE_PATH=./data/icl.db
Security Configuration
Before production deployment, update the following in source code:
src/lib/session.ts:14 - Move session password to environment variable:
password: process.env.SESSION_PASSWORD || "fallback-dev-key",
src/lib/session.ts:17 - Enable secure cookies for HTTPS:
secure: process.env.NODE_ENV === "production", // true in production
Build Process
This creates an optimized production build in .next/.
Check that the build completed successfully:
You should see directories like server, static, and standalone (if configured).
Database Setup
mkdir -p data
chmod 755 data
Apply database schema migrations:
This creates data/icl.db with all tables using Drizzle Kit.
Check that the database was created:
sqlite3 data/icl.db ".tables"
clients locations quotations
commercial_agreements port_rates users
pricing_netos
Deployment Options
Create PM2 Ecosystem File
Create ecosystem.config.js:
module.exports = {
apps: [{
name: 'icl-cotizaciones',
script: 'node_modules/next/dist/bin/next',
args: 'start',
cwd: '/path/to/iclapp',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 3000,
SESSION_PASSWORD: 'your-secure-password',
},
}],
};
pm2 start ecosystem.config.js
pm2 save
pm2 startup
pm2 status
pm2 logs icl-cotizaciones
pm2 monit
Option 2: Docker Deployment
Create a Dockerfile:
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
# Build the application
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/drizzle ./drizzle
RUN mkdir -p /app/data && chown nextjs:nodejs /app/data
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["npm", "start"]
Create docker-compose.yml:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- SESSION_PASSWORD=${SESSION_PASSWORD}
volumes:
- ./data:/app/data
restart: unless-stopped
docker-compose build
docker-compose up -d
docker-compose exec web npm run db:migrate
Option 3: Systemd Service
Create /etc/systemd/system/icl-cotizaciones.service:
[Unit]
Description=ICL Cotizaciones
After=network.target
[Service]
Type=simple
User=iclapp
WorkingDirectory=/opt/iclapp
Environment="NODE_ENV=production"
Environment="PORT=3000"
Environment="SESSION_PASSWORD=your-secure-password"
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=icl-cotizaciones
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable icl-cotizaciones
sudo systemctl start icl-cotizaciones
sudo systemctl status icl-cotizaciones
sudo journalctl -u icl-cotizaciones -f
Reverse Proxy Configuration
Create /etc/nginx/sites-available/icl-cotizaciones:
server {
listen 80;
server_name cotizaciones.iclsa.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name cotizaciones.iclsa.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/cotizaciones.iclsa.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cotizaciones.iclsa.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 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;
location / {
proxy_pass http://localhost:3000;
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_cache_bypass $http_upgrade;
}
}
sudo ln -s /etc/nginx/sites-available/icl-cotizaciones /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d cotizaciones.iclsa.com
Post-Deployment Verification
curl http://localhost:3000
Expected: HTML response from Next.js
sqlite3 data/icl.db "SELECT COUNT(*) FROM users;"
Check that cookies are set with proper security flags:
curl -v https://cotizaciones.iclsa.com | grep -i "set-cookie"
Expected: HttpOnly; Secure; SameSite=Lax
# PM2
pm2 logs icl-cotizaciones --lines 50
# Systemd
sudo journalctl -u icl-cotizaciones -n 50
# Docker
docker-compose logs --tail=50 -f
Enable Database Backups
See Database Management for backup configuration.
Optimize SQLite WAL mode performance:
PRAGMA wal_autocheckpoint = 1000;
PRAGMA journal_size_limit = 67108864;
Enable Response Caching
Add caching headers to Next.js responses for static assets.
Security Checklist
Rollback Procedure
If deployment fails:
pm2 stop icl-cotizaciones
# or
sudo systemctl stop icl-cotizaciones
cp data/icl.db.backup data/icl.db
git checkout <previous-tag>
npm install
npm run build
pm2 restart icl-cotizaciones
curl http://localhost:3000
pm2 logs icl-cotizaciones
Troubleshooting
For deployment issues, see Troubleshooting Guide.