Разделы документации
ОбзорБыстрый стартРедакции и возможностиМодели и поляРедактор контентаМедиатекаЛокализацияПубликация и работа командыAPI, SDK и генерация типовРасширения и инструментыРабочие проектыЗадания, вебхуки и наблюдаемостьАудит и управление даннымиСоветники и доверие к плагинамКорпоративный входПространства, квоты и масштабированиеCommerce и PortalПрава и безопасностьРазвёртывание и обновленияЛицензии и установка пакетовТекущие ограниченияПомощь и диагностикаДанные в кабинетеCore CMSDeveloper PlatformProduction UseWorkflowOperationsComplianceAI AssistantsPlugin TrustEnterprise IdentityEnterprise ScaleEnterprise DeploymentCommerce BundlePortal BundleNotty CMS DocumentationAuth & SecurityContent ModelingDeploymentEcosystem & Packaging ConventionsEditions and First-party ModulesExtensibilityGetting StartedMedia ManagementModule Extraction PathDraft & PublishUpgrade GuideWebhooks & IntegrationsCookbook: Blog with Next.jsCookbook: Custom PluginCookbook: Multilingual SiteOperations DocsBackup AutomationDeployment BlueprintsRunbook — Восстановление БД из бэкапаRunbook — Плановый деплойRunbook — Реакция на инцидентRunbook — Откат релизаRunbook — Горизонтальное масштабированиеRunbook — Ротация секретовRunbook — Major upgradeSecrets ManagementNotty CMS — Capability MapNotty Configuration ModelGenerated App ContractComponents and Dynamic ZonesMiddleware SystemPerformance & Scaling ToolkitDisaster Recovery PlaybookDistribution Model
Документация / Технический справочник

Deployment

Техническое руководство из исходного проекта Notty. Примеры, параметры и эксплуатационные ограничения.

Все редакцииОбновлено 2026-09-30

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

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
  • .env in .gitignore
  • Process manager (PM2/systemd) or container orchestration
  • Monitoring and logging

Источник: docs/guide/deployment.md. Снимок документации исходного проекта. Технический справочник сохраняет язык оригинала.