Deployment
Техническое руководство из исходного проекта Notty. Примеры, параметры и эксплуатационные ограничения.
This guide covers configuring Notty CMS for production, security hardening, and deployment strategies.
Production Configuration
Environment Variables
Create a .env file with production settings:
NODE_ENV=production
# Database (use PostgreSQL or MySQL for production)
DATABASE_URL=postgresql://user:password@db-host:5432/notty
# Security
JWT_SECRET=your-strong-random-secret-at-least-32-chars
NITRO_JWT_SECRET=your-strong-random-secret-at-least-32-chars
PORT=2102
HOST=0.0.0.0
# Storage
STORAGE_TYPE=local
STORAGE_UPLOAD_DIR=uploads
STORAGE_BASE_URL=/api/uploads
# Optional: OAuth
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
Generate a strong JWT secret:
openssl rand -base64 32
Configuration File
// notty.config.ts
import { defineConfig } from '@notty/core';
export default defineConfig({
database: {
url: process.env.DATABASE_URL,
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: '7d',
},
server: {
port: Number(process.env.PORT) || 2102,
host: process.env.HOST || '0.0.0.0',
},
storage: {
type: 'local',
uploadDir: 'uploads',
},
});
Configuration Priority
Environment variables > notty.config.ts > Database (admin settings) > Defaults
Env vars always win. Settings changed in the admin UI are stored in the database but can be overridden by env vars. See Configuration Model for full details.
Startup Validation
Notty validates configuration on every startup:
| Check | Development | Production |
|---|---|---|
Missing DATABASE_URL |
Fatal | Fatal |
Invalid DATABASE_URL |
Fatal | Fatal |
Missing JWT_SECRET |
Warning | Fatal |
Weak JWT_SECRET (<16 chars) |
— | Warning |
Invalid PORT |
Fatal | Fatal |
| OAuth ID without secret | Warning | Warning |
In production, the server refuses to start without a proper JWT_SECRET and DATABASE_URL.
Database
PostgreSQL (Recommended)
DATABASE_URL=postgresql://user:password@localhost:5432/notty
Best for production: robust, scalable, full-text search support.
MySQL / MariaDB
DATABASE_URL=mysql://user:password@localhost:3306/notty
Good alternative if you have existing MySQL infrastructure.
SQLite
DATABASE_URL=sqlite://./data/notty.db
Development and prototyping only. Not recommended for production due to:
- Single-writer concurrency
- No network access
- Limited full-text search
Database Sync
Notty auto-syncs database tables from schemas on startup. You can also manage this manually:
# Check sync status
curl http://localhost:2102/api/database/status \
-H "Authorization: Bearer $TOKEN"
# Preview changes before applying
curl http://localhost:2102/api/database/preview \
-H "Authorization: Bearer $TOKEN"
# Apply changes
curl -X POST http://localhost:2102/api/database/safe-sync \
-H "Authorization: Bearer $TOKEN"
Reverse Proxy
Nginx
server {
listen 80;
server_name cms.example.com;
# Redirect to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name cms.example.com;
ssl_certificate /etc/ssl/certs/cms.example.com.pem;
ssl_certificate_key /etc/ssl/private/cms.example.com.key;
# Upload size limit
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:2102;
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;
# WebSocket support (for real-time features)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Cache static assets
location /admin/assets/ {
proxy_pass http://127.0.0.1:2102;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Caddy
cms.example.com {
reverse_proxy localhost:2102
}
Caddy handles HTTPS automatically with Let's Encrypt.
Docker
Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm@9.12.1 --no-audit --no-fund --loglevel=error \
&& pnpm config set update-notifier false \
&& pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.output ./.output
COPY --from=builder /app/schemas ./schemas
COPY --from=builder /app/notty.config.ts ./
RUN mkdir -p uploads data
EXPOSE 2102
CMD ["node", ".output/server/index.mjs"]
Docker Compose
version: '3.8'
services:
notty:
build: .
ports:
- '2102:2102'
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://notty:notty@postgres:5432/notty
- NITRO_DATABASE_URL=postgresql://notty:notty@postgres:5432/notty
- JWT_SECRET=${JWT_SECRET}
- NITRO_JWT_SECRET=${JWT_SECRET}
- HOST=0.0.0.0
volumes:
- uploads:/app/uploads
- schemas:/app/schemas
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_USER=notty
- POSTGRES_PASSWORD=notty
- POSTGRES_DB=notty
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U notty']
interval: 5s
timeout: 5s
retries: 5
volumes:
uploads:
schemas:
pgdata:
Start:
JWT_SECRET=$(openssl rand -base64 32) docker compose up -d
Process Manager
For non-Docker deployments, use PM2 to keep Notty running:
npm install -g pm2
pm2 start .output/server/index.mjs --name notty
pm2 save
pm2 startup
Security Hardening
CORS
Restrict cross-origin requests in production:
NOTTY_CORS_ORIGINS=["https://yoursite.com","https://admin.yoursite.com"]
Or via admin settings (Settings → API → CORS).
Rate Limiting
Enable rate limiting to protect against abuse:
NOTTY_RATE_LIMIT_ENABLED=true
NOTTY_RATE_LIMIT_MAX=100
NOTTY_RATE_LIMIT_WINDOW=60000
100 requests per minute per IP.
Environment Variable Locking
When a runtime setting has a corresponding env var, it becomes locked in the admin UI and cannot be changed through the API. This prevents accidental overrides in production.
Backups
Database
PostgreSQL:
pg_dump -U notty notty > backup_$(date +%Y%m%d).sql
MySQL:
mysqldump -u notty -p notty > backup_$(date +%Y%m%d).sql
Uploads
Back up the upload directory:
tar -czf uploads_$(date +%Y%m%d).tar.gz uploads/
Automated Backup Script
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/backups/notty
mkdir -p $BACKUP_DIR
# Database
pg_dump -U notty notty > $BACKUP_DIR/db_$DATE.sql
# Uploads
tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz -C /app uploads/
# Schemas
tar -czf $BACKUP_DIR/schemas_$DATE.tar.gz -C /app schemas/
# Cleanup backups older than 30 days
find $BACKUP_DIR -mtime +30 -delete
Health Check
Notty provides a database status endpoint:
curl http://localhost:2102/api/database/status
Use this for container health checks or load balancer probes.
Production Checklist
-
NODE_ENV=production - Strong
JWT_SECRET(32+ random characters) - PostgreSQL or MySQL (not SQLite)
- Database credentials in env vars
- HTTPS via reverse proxy
- CORS restricted to your domains
- Rate limiting enabled
- Upload size limits configured
- Regular database backups
-
.envin.gitignore - Process manager (PM2/systemd) or container orchestration
- Monitoring and logging
Источник: docs/guide/deployment.md. Снимок документации исходного проекта. Технический справочник сохраняет язык оригинала.