Deployment
This guide covers deploying Trokky to various hosting platforms and production best practices.
Pre-Deployment Checklist
Section titled “Pre-Deployment Checklist”Before deploying to production:
- Set strong JWT secret
- Configure secure admin credentials
- Enable HTTPS
- Set appropriate CORS origins
- Configure production storage adapter
- Build Studio for production
- Set up monitoring and logging
- Configure backups
Environment Variables
Section titled “Environment Variables”Required Variables
Section titled “Required Variables”# SecurityTROKKY_JWT_SECRET=your-256-bit-secretTROKKY_ADMIN_USERNAME=adminTROKKY_ADMIN_PASSWORD=secure-password
# ServerNODE_ENV=productionPORT=3000Optional Variables
Section titled “Optional Variables”# Storage (S3)AWS_REGION=us-east-1AWS_ACCESS_KEY_ID=your-keyAWS_SECRET_ACCESS_KEY=your-secretS3_BUCKET=your-bucketDYNAMODB_TABLE=your-table
# CORSCORS_ORIGIN=https://your-domain.com
# LoggingLOG_LEVEL=infoPlatform Guides
Section titled “Platform Guides”Node.js Server
Section titled “Node.js Server”Deploy to any Node.js hosting (Railway, Render, DigitalOcean, etc.)
1. Build for Production
Section titled “1. Build for Production”npm run buildnpm run studio:build2. Production Server
Section titled “2. Production Server”import express from 'express';import { TrokkyExpress } from '@trokky/express';import { schemas } from './schemas.js';
const app = express();
// Trust proxy for HTTPS detectionapp.set('trust proxy', 1);
const trokky = await TrokkyExpress.create({ schemas, storage: { adapter: 's3', region: process.env.AWS_REGION, bucket: process.env.S3_BUCKET, tableName: process.env.DYNAMODB_TABLE, }, security: { jwtSecret: process.env.TROKKY_JWT_SECRET, adminUser: { username: process.env.TROKKY_ADMIN_USERNAME, password: process.env.TROKKY_ADMIN_PASSWORD, }, }, server: { cors: { origin: process.env.CORS_ORIGIN, credentials: true, }, }, studio: { enabled: true, },});
trokky.mount(app);
const PORT = process.env.PORT || 3000;app.listen(PORT, () => { console.log(`Server running on port ${PORT}`);});3. Dockerfile
Section titled “3. Dockerfile”FROM node:20-alpine
WORKDIR /app
COPY package*.json ./RUN npm ci --only=production
COPY dist ./distCOPY studio-build ./studio-build
ENV NODE_ENV=productionEXPOSE 3000
CMD ["node", "dist/server.js"]Vercel (Next.js)
Section titled “Vercel (Next.js)”Deploy as a Next.js application with API routes.
1. Install Next.js Integration
Section titled “1. Install Next.js Integration”npm install @trokky/nextjs2. Create API Route
Section titled “2. Create API Route”import { TrokkyNextjs } from '@trokky/nextjs';import { schemas } from '@/schemas';
const trokky = TrokkyNextjs.create({ schemas, storage: { adapter: 's3', region: process.env.AWS_REGION!, bucket: process.env.S3_BUCKET!, tableName: process.env.DYNAMODB_TABLE!, }, security: { jwtSecret: process.env.TROKKY_JWT_SECRET!, },});
export const { GET, POST, PUT, DELETE, PATCH } = trokky.handlers();3. Environment Variables
Section titled “3. Environment Variables”Add to Vercel dashboard or .env.local:
TROKKY_JWT_SECRET=...AWS_REGION=...S3_BUCKET=...DYNAMODB_TABLE=...Cloudflare Workers
Section titled “Cloudflare Workers”Deploy to the edge with Cloudflare Workers.
1. Install Cloudflare Integration
Section titled “1. Install Cloudflare Integration”npm install @trokky/cloudflare2. Worker Entry
Section titled “2. Worker Entry”import { TrokkyCloudflare } from '@trokky/cloudflare';import { schemas } from './schemas';
export default { async fetch(request: Request, env: Env) { const trokky = TrokkyCloudflare.create({ schemas, storage: { adapter: 'cloudflare', d1: env.DB, r2: env.STORAGE, }, security: { jwtSecret: env.JWT_SECRET, }, });
return trokky.handle(request); },};3. Wrangler Configuration
Section titled “3. Wrangler Configuration”name = "trokky-cms"main = "src/index.ts"compatibility_date = "2024-01-01"
[[d1_databases]]binding = "DB"database_name = "trokky"database_id = "xxx"
[[r2_buckets]]binding = "STORAGE"bucket_name = "trokky-media"
[vars]JWT_SECRET = "your-secret"AWS (EC2/ECS)
Section titled “AWS (EC2/ECS)”EC2 with PM2
Section titled “EC2 with PM2”# Install PM2npm install -g pm2
# Start applicationpm2 start dist/server.js --name trokky
# Configure startuppm2 startuppm2 saveECS Task Definition
Section titled “ECS Task Definition”{ "family": "trokky", "containerDefinitions": [ { "name": "trokky", "image": "your-ecr-repo/trokky:latest", "portMappings": [ { "containerPort": 3000 } ], "environment": [ { "name": "NODE_ENV", "value": "production" } ], "secrets": [ { "name": "TROKKY_JWT_SECRET", "valueFrom": "arn:aws:..." } ] } ]}HTTPS Configuration
Section titled “HTTPS Configuration”Behind Load Balancer
Section titled “Behind Load Balancer”// Trust proxy headersapp.set('trust proxy', 1);
// Force HTTPS redirectapp.use((req, res, next) => { if (req.headers['x-forwarded-proto'] !== 'https') { return res.redirect(`https://${req.headers.host}${req.url}`); } next();});Direct SSL
Section titled “Direct SSL”import https from 'https';import fs from 'fs';
const options = { key: fs.readFileSync('/path/to/key.pem'), cert: fs.readFileSync('/path/to/cert.pem'),};
https.createServer(options, app).listen(443);Security Hardening
Section titled “Security Hardening”Rate Limiting
Section titled “Rate Limiting”import rateLimit from 'express-rate-limit';
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // requests per window});
app.use('/api', limiter);Security Headers
Section titled “Security Headers”import helmet from 'helmet';
app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], }, },}));Input Validation
Section titled “Input Validation”Trokky validates all input against schemas. Additional validation:
import { body, validationResult } from 'express-validator';
app.post('/api/custom', body('email').isEmail(), body('name').trim().notEmpty(), (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // ... });Performance
Section titled “Performance”Caching
Section titled “Caching”// API response cachingapp.use('/api/documents', (req, res, next) => { if (req.method === 'GET') { res.set('Cache-Control', 'public, max-age=60'); } next();});CDN for Media
Section titled “CDN for Media”Configure your CDN to serve from /media:
media: { baseUrl: 'https://cdn.example.com',}Compression
Section titled “Compression”import compression from 'compression';app.use(compression());Monitoring
Section titled “Monitoring”Health Check
Section titled “Health Check”app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() });});Logging
Section titled “Logging”import morgan from 'morgan';
// Request loggingapp.use(morgan('combined'));
// Error loggingapp.use((err, req, res, next) => { console.error({ error: err.message, stack: err.stack, url: req.url, method: req.method, }); res.status(500).json({ error: 'Internal server error' });});Backups
Section titled “Backups”Filesystem Backup
Section titled “Filesystem Backup”# Daily backup script#!/bin/bashDATE=$(date +%Y-%m-%d)tar -czf backup-$DATE.tar.gz content/ uploads/aws s3 cp backup-$DATE.tar.gz s3://backups/trokky/Database Backup
Section titled “Database Backup”For S3/DynamoDB:
# Export DynamoDB tableaws dynamodb export-table-to-point-in-time \ --table-arn arn:aws:dynamodb:... \ --s3-bucket backups \ --s3-prefix dynamodb/Scaling
Section titled “Scaling”Horizontal Scaling
Section titled “Horizontal Scaling”Trokky is stateless and can run multiple instances:
- Use external session store (Redis) for JWT blacklist
- Use S3 or Cloudflare R2 for media storage
- Put a load balancer in front
Database Scaling
Section titled “Database Scaling”- DynamoDB: Enable auto-scaling
- Cloudflare D1: Use read replicas
Next Steps
Section titled “Next Steps”- Configuration Reference - Full configuration options
- HTTP API - API documentation
- Troubleshooting - Common issues and solutions