BaasixBaasix

Deployment Guide

This guide provides comprehensive instructions for deploying Baasix in various environments using the @baasix/baasix npm package.

Table of Contents

  1. Overview
  2. Prerequisites
  3. Quick Start
  4. Environment Configuration
  5. Deployment Methods
  6. Production Considerations
  7. Monitoring and Maintenance
  8. 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:

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 init

The 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 -y

2. Install Baasix

npm install @baasix/baasix

3. 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 start

Environment 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=info

AWS 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.com

SSO & 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 configurations

Deployment Methods

PM2 is a production process manager for Node.js with built-in load balancing.

Install PM2

npm install -g pm2

Create 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 startup

PM2 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 application

Method 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=3

Method 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: ClusterIP

Horizontal 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: 70

Method 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 pm2

2. 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";
EOF

3. 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 startup

Multi-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:6379

Service-Specific Redis Purposes

ServiceConfigPurpose
Socket.IOSOCKET_REDIS_*WebSocket message broadcasting, WAL leader election
TasksTASK_REDIS_*Distributed locking to prevent duplicate task execution
CacheCACHE_REDIS_URLShared query cache across instances

Why Separate Redis Configurations?

Each service can use the same Redis instance but has separate configuration because:

  1. Flexibility: Enable only what you need (e.g., Socket.IO scaling without Redis cache)
  2. Isolation: Use different Redis instances for different workloads
  3. 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 = 64MB

Redis Configuration

# redis.conf production settings
maxmemory 256mb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000

Nginx 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"
fi

Log 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 true

Backup 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 baasix

Troubleshooting

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-*.log

Redis 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.log

Application Issues

# Check PM2 status
pm2 status
pm2 logs baasix

# View detailed logs
tail -f /var/www/baasix/logs/error.log

# Restart application
pm2 restart baasix

Performance 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 logs

Sample 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.js and package.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

On this page