Deployment Guide
This guide provides comprehensive instructions for deploying Baasix in various environments using the @baasix/baasix npm package.
Table of Contents
- Overview
- Prerequisites
- Quick Start
- Environment Configuration
- Deployment Methods
- Production Considerations
- Monitoring and Maintenance
- Troubleshooting
Overview
Baasix is distributed as an npm package (@baasix/baasix) that you install in your project. This approach allows you to:
- Keep your server configuration simple
- Easily update to new versions
- Add custom extensions without modifying core code
- Deploy using any Node.js hosting method
Prerequisites
System Requirements
- Node.js: Version 18+ (LTS recommended)
- PostgreSQL: Version 14+ (with PostGIS extension for geospatial features)
- Redis: Version 6+ (for caching and Socket.IO scaling)
- Memory: Minimum 512MB RAM (recommended: 2GB+ for production)
- Storage: Minimum 1GB available disk space
Optional Dependencies
- Docker: Version 20+ (for containerized deployment)
- PM2: For process management in production
- Nginx: For reverse proxy and load balancing
Quick Start
You have two options to create a Baasix project:
Option 1: Using the CLI (Recommended)
The Baasix CLI provides interactive project scaffolding with all configuration options:
# Using npx (no installation required)
npx baasix init
# Or install globally first
npm install -g baasix
baasix initThe CLI will guide you through:
- Project name and template selection (API, Next.js App Router, Next.js Pages)
- Database URL configuration
- Multi-tenancy, WebSocket, and storage options
- Authentication providers (Google, GitHub, etc.)
- Cache adapter selection (Memory, Redis)
For complete CLI documentation, see the CLI Guide.
Option 2: Manual Setup
1. Create Your Project
mkdir my-baasix-app
cd my-baasix-app
npm init -y2. Install Baasix
npm install @baasix/baasix3. Create Server Entry Point
Create server.js:
import { startServer } from '@baasix/baasix';
// Basic usage - pretty printing in dev, JSON output in production
startServer().catch((error) => {
console.error('Failed to start server:', error);
process.exit(1);
});Advanced Server Configuration
import { startServer } from '@baasix/baasix';
startServer({
port: 8056,
logger: {
level: 'info', // 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
pretty: false, // Set to true for human-readable output
// Custom transport example (e.g., Datadog)
// transport: {
// target: "pino-datadog-transport",
// options: {
// apiKey: process.env.DD_API_KEY,
// service: "baasix-api"
// }
// }
},
}).catch((error) => {
console.error('Failed to start server:', error);
process.exit(1);
});4. Update package.json
{
"name": "my-baasix-app",
"version": "1.0.0",
"type": "module",
"main": "server.js",
"scripts": {
"start": "tsx server.js",
"dev": "tsx watch server.js"
},
"dependencies": {
"@baasix/baasix": "latest"
},
"devDependencies": {
"tsx": "^4.16.0"
}
}6. Start the Server
npm startEnvironment Configuration
Core Environment Variables
# =============================================================================
# Application Settings
# =============================================================================
NODE_ENV=production
PORT=8056
SECRET_KEY=your-super-secret-jwt-key-min-32-chars
# =============================================================================
# Database Configuration (PostgreSQL)
# =============================================================================
DATABASE_URL="postgresql://postgres:yourpassword@localhost:5432/baasix"
# Connection retry settings
DATABASE_MAX_RETRY=3
DATABASE_RETRY_INTERVAL=5000
# Pool settings
DATABASE_POOL_MAX=20
DATABASE_POOL_ACQUIRE=30000
DATABASE_POOL_IDLE=10000
DATABASE_POOL_EVICT=60000
# Enable SSL for database connection (recommended for production)
# DATABASE_SSL_CERTIFICATE=/path/to/ca-certificate.crt
# DATABASE_SSL_REJECT_UNAUTHORIZED=true
# =============================================================================
# Cache Configuration
# =============================================================================
CACHE_ENABLED=true
CACHE_ADAPTER=redis
CACHE_REDIS_URL=redis://localhost:6379
CACHE_TTL=300
# =============================================================================
# CORS & Security
# =============================================================================
AUTH_CORS_ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
AUTH_CORS_ALLOW_ANY_PORT=false
AUTH_CORS_CREDENTIALS=true
# =============================================================================
# Rate Limiting
# =============================================================================
RATE_LIMIT=100
RATE_LIMIT_INTERVAL=5000
# RATE_LIMIT_BY_USER=true # Rate limit by user ID instead of IP (useful when multiple users share same network)
# =============================================================================
# Cookies (Production Settings)
# =============================================================================
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAME_SITE=strict
# AUTH_COOKIE_DOMAIN=.yourdomain.com
# =============================================================================
# File Storage Configuration
# =============================================================================
STORAGE_SERVICES_ENABLED="LOCAL"
STORAGE_DEFAULT_SERVICE="LOCAL"
LOCAL_STORAGE_DRIVER=LOCAL
LOCAL_STORAGE_PATH=./uploads
# =============================================================================
# Email Configuration (Optional)
# =============================================================================
# MAIL_SENDERS_ENABLED=SENDGRID
# MAIL_DEFAULT_SENDER=SENDGRID
# SENDGRID_SMTP_HOST=smtp.sendgrid.net
# SENDGRID_SMTP_PORT=587
# SENDGRID_SMTP_USER=apikey
# SENDGRID_SMTP_PASS=your_sendgrid_api_key
# SENDGRID_FROM_ADDRESS="Your App <noreply@yourdomain.com>"
# =============================================================================
# Logging
# =============================================================================
LOG_LEVEL=infoAWS S3 Storage Configuration
STORAGE_DEFAULT_SERVICE=s3
S3_STORAGE_ACCESS_KEY_ID=your-access-key-id
S3_STORAGE_SECRET_ACCESS_KEY=your-secret-access-key
S3_STORAGE_REGION=us-east-1
S3_STORAGE_BUCKET=your-bucket-name
S3_STORAGE_ENDPOINT=https://s3.amazonaws.comSSO & Social Authentication
For enabling social authentication providers, see the SSO & Social Authentication Guide.
AUTH_SERVICES_ENABLED=LOCAL,GOOGLE,GITHUB,FACEBOOK,APPLE
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# ... other provider configurationsDeployment Methods
Method 1: PM2 Deployment (Recommended)
PM2 is a production process manager for Node.js with built-in load balancing.
Install PM2
npm install -g pm2Create PM2 Ecosystem File
Create ecosystem.config.js in your project root:
module.exports = {
apps: [
{
name: 'baasix',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 8056,
},
error_file: './logs/error.log',
out_file: './logs/out.log',
time: true,
max_memory_restart: '500M',
},
],
};Start with PM2
# Start application
pm2 start ecosystem.config.js --env production
# Save process list for auto-restart
pm2 save
# Setup auto-start on system boot
pm2 startupPM2 Commands
pm2 status # View process status
pm2 logs baasix # View logs
pm2 monit # Monitor CPU/Memory
pm2 reload baasix # Zero-downtime restart
pm2 stop baasix # Stop applicationMethod 2: Docker Deployment
Create Dockerfile
FROM node:18-alpine
# Install system dependencies
RUN apk add --no-cache bash curl postgresql-client
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install production dependencies
RUN npm ci --only=production
# Copy application files
COPY server.js ./
COPY .env* ./
COPY extensions/ ./extensions/ 2>/dev/null || true
# Create uploads directory
RUN mkdir -p uploads
# Create non-root user
RUN addgroup -g 1001 -S baasix && \
adduser -S baasix -u 1001 -G baasix
RUN chown -R baasix:baasix /app
USER baasix
# Expose port
EXPOSE 8056
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:8056/ || exit 1
# Start application
CMD ["node", "server.js"]Create Docker Compose
version: '3.8'
services:
app:
build: .
ports:
- '8056:8056'
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/baasix
- CACHE_ADAPTER=redis
- CACHE_REDIS_URL=redis://redis:6379
env_file:
- .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ./uploads:/app/uploads
- ./extensions:/app/extensions
restart: unless-stopped
postgres:
image: postgis/postgis:15-3.3-alpine
environment:
POSTGRES_DB: baasix
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres -d baasix']
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
redis_data:Deploy with Docker
# Build and start
docker-compose up -d
# View logs
docker-compose logs -f app
# Scale application
docker-compose up -d --scale app=3Method 3: Kubernetes Deployment
Create Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: baasix
namespace: baasix
spec:
replicas: 3
selector:
matchLabels:
app: baasix
template:
metadata:
labels:
app: baasix
spec:
containers:
- name: baasix
image: your-registry/baasix:latest
ports:
- containerPort: 8056
envFrom:
- configMapRef:
name: baasix-config
- secretRef:
name: baasix-secrets
resources:
requests:
cpu: '250m'
memory: '256Mi'
limits:
cpu: '1000m'
memory: '512Mi'
livenessProbe:
httpGet:
path: /
port: 8056
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 8056
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: baasix
namespace: baasix
spec:
selector:
app: baasix
ports:
- port: 80
targetPort: 8056
type: ClusterIPHorizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: baasix-hpa
namespace: baasix
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: baasix
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Method 4: Manual Server Deployment
1. Prepare the Server
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js 18
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install PostgreSQL with PostGIS
sudo apt install postgresql postgresql-contrib postgis -y
# Install Redis
sudo apt install redis-server -y
# Install PM2
sudo npm install -g pm22. Setup Database
sudo -u postgres psql << EOF
CREATE USER baasix_user WITH PASSWORD 'secure_password';
CREATE DATABASE baasix_production OWNER baasix_user;
GRANT ALL PRIVILEGES ON DATABASE baasix_production TO baasix_user;
\c baasix_production
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
EOF3. Deploy Application
# Create application directory
sudo mkdir -p /var/www/baasix
cd /var/www/baasix
# Create project files
npm init -y
npm install @baasix/baasix
# Create server.js
cat > server.js << 'EOF'
import { startServer } from "@baasix/baasix";
startServer().catch((error) => {
console.error("Failed to start server:", error);
process.exit(1);
});
EOF
# Update package.json to use ES modules
npm pkg set type="module"
# Configure environment
cp .env.example .env
nano .env
# Create directories
mkdir -p uploads logs extensions
# Start with PM2
pm2 start server.js --name baasix
pm2 save
pm2 startupMulti-Instance Deployment (Scaling)
When running multiple instances of Baasix (PM2 cluster mode, Kubernetes replicas, etc.), you need to configure Redis for proper coordination between instances.
Required Redis Configuration
# =============================================================================
# Multi-Instance Configuration
# =============================================================================
# Socket.IO - Required for WebSocket broadcasting across instances
SOCKET_ENABLED=true
SOCKET_REDIS_ENABLED=true
SOCKET_REDIS_URL=redis://localhost:6379
SOCKET_REDIS_KEY=socket.io
# Tasks - Required for distributed task locking (prevent duplicate execution)
TASK_SERVICE_ENABLED=true
TASK_REDIS_ENABLED=true
TASK_REDIS_URL=redis://localhost:6379
# Cache - Optional but recommended for consistency
CACHE_ADAPTER=redis
CACHE_REDIS_URL=redis://localhost:6379Service-Specific Redis Purposes
| Service | Config | Purpose |
|---|---|---|
| Socket.IO | SOCKET_REDIS_* | WebSocket message broadcasting, WAL leader election |
| Tasks | TASK_REDIS_* | Distributed locking to prevent duplicate task execution |
| Cache | CACHE_REDIS_URL | Shared query cache across instances |
Why Separate Redis Configurations?
Each service can use the same Redis instance but has separate configuration because:
- Flexibility: Enable only what you need (e.g., Socket.IO scaling without Redis cache)
- Isolation: Use different Redis instances for different workloads
- Clarity: Clear understanding of what each Redis connection is for
PostgreSQL WAL Considerations
When using real-time features with multiple instances:
- PostgreSQL WAL replication slots only allow one consumer
- BAASIX automatically handles leader election via Redis
- Only the leader instance consumes WAL changes
- Changes are broadcast to all instances via Redis pub/sub
- Automatic failover when the leader goes down
Example: PM2 Cluster with Redis
# .env for multi-instance PM2 cluster
NODE_ENV=production
PORT=8056
# Database
DATABASE_URL="postgresql://user:pass@localhost:5432/baasix"
# Socket.IO with Redis (required for cluster)
SOCKET_ENABLED=true
SOCKET_REDIS_ENABLED=true
SOCKET_REDIS_URL=redis://localhost:6379
# Tasks with Redis (required for cluster)
TASK_SERVICE_ENABLED=true
TASK_REDIS_ENABLED=true
TASK_REDIS_URL=redis://localhost:6379
# Cache with Redis (recommended for cluster)
CACHE_ENABLED=true
CACHE_ADAPTER=redis
CACHE_REDIS_URL=redis://localhost:6379// ecosystem.config.js
module.exports = {
apps: [
{
name: 'baasix',
script: './server.js',
instances: 'max', // Uses all CPU cores
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
},
},
],
};Production Considerations
Security Checklist
- Use HTTPS/TLS certificates
- Set
AUTH_COOKIE_SECURE=true - Configure proper CORS origins (
AUTH_CORS_ALLOWED_ORIGINS) - Enable rate limiting
- Use strong, unique
SECRET_KEY(min 32 characters) - Enable database SSL connection (
DATABASE_SSL_CERTIFICATE) - Implement proper firewall rules
- Keep dependencies updated
- Use environment variables for secrets
- Enable audit logging
Performance Optimization
Database Optimization
-- Create indexes for better performance
CREATE INDEX idx_items_created_at ON items(created_at);
CREATE INDEX idx_items_tenant_id ON items(tenant_id);
CREATE INDEX idx_notifications_user_seen ON notifications(user_id, seen);
-- Configure PostgreSQL for production
-- In postgresql.conf:
shared_buffers = 256MB
effective_cache_size = 1GB
maintenance_work_mem = 64MBRedis Configuration
# redis.conf production settings
maxmemory 256mb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000Nginx Reverse Proxy
upstream baasix_backend {
server 127.0.0.1:8056;
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/ssl/certs/yourdomain.com.crt;
ssl_certificate_key /etc/ssl/private/yourdomain.com.key;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# Gzip compression
gzip on;
gzip_types text/plain application/json application/javascript text/css;
# File upload size
client_max_body_size 50M;
location / {
proxy_pass http://baasix_backend;
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;
}
# Socket.IO WebSocket support
location /socket.io/ {
proxy_pass http://baasix_backend;
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_read_timeout 86400s;
}
}Monitoring and Maintenance
Health Check
# Simple health check
curl http://localhost:8056/
# Health check script
#!/bin/bash
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8056/)
if [ $RESPONSE -eq 200 ]; then
echo "$(date): Service is healthy"
else
echo "$(date): Service is unhealthy - HTTP $RESPONSE"
fiLog Management
# PM2 logs
pm2 logs baasix
# Setup log rotation with PM2
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress trueBackup Strategy
#!/bin/bash
# backup.sh
DATE=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/backups"
# Database backup
pg_dump -h localhost -U baasix_user baasix_production | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"
# Files backup
tar -czf "$BACKUP_DIR/files_$DATE.tar.gz" /var/www/baasix/uploads
# Clean old backups (keep 30 days)
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete
echo "Backup completed: $DATE"Updating Baasix
# Update to latest version
cd /var/www/baasix
npm update @baasix/baasix
# Restart with zero downtime
pm2 reload baasixTroubleshooting
Common Issues
Database Connection Issues
# Check PostgreSQL status
sudo systemctl status postgresql
# Test database connection
psql -h localhost -U baasix_user -d baasix_production -c "SELECT version();"
# Check database logs
sudo tail -f /var/log/postgresql/postgresql-*.logRedis Connection Issues
# Check Redis status
sudo systemctl status redis
# Test Redis connection
redis-cli ping
# Check Redis logs
sudo tail -f /var/log/redis/redis-server.logApplication Issues
# Check PM2 status
pm2 status
pm2 logs baasix
# View detailed logs
tail -f /var/www/baasix/logs/error.log
# Restart application
pm2 restart baasixPerformance Issues
# Monitor system resources
htop
# Check database connections
sudo -u postgres psql -c "SELECT * FROM pg_stat_activity WHERE state = 'active';"Project Structure
When using Baasix as a package, your project should have this structure:
my-baasix-app/
├── server.js # Main entry point
├── package.json # Dependencies
├── .env # Environment configuration
├── extensions/ # Custom extensions (optional)
│ ├── baasix-hook-*/ # Hook extensions
│ └── baasix-endpoint-*/ # Endpoint extensions
├── uploads/ # Local file storage
└── logs/ # Application logsSample Repository
For a complete working example with all deployment configurations, check out our sample repository:
👉 github.com/baasix/baasix/samples/sample
The sample includes:
- Ready-to-use
server.jsandpackage.json - Environment configuration templates
- Docker deployment files (Dockerfile, docker-compose)
- PM2 ecosystem configurations
- Kubernetes manifests (deployment, service, ingress, HPA)
- Nginx reverse proxy configurations
- Example hook and endpoint extensions
Related Documentation
- Settings Routes - Application configuration
- Authentication Routes - Security setup
- SSO Authentication Guide - Social login setup
- Extensions Guide - Creating custom extensions
- Hooks Guide - Hook system documentation