Skip to main content

Production Setup Guide

This guide will help you set up Chirp for production use with proper security, performance, and reliability configurations.

Prerequisites

Before starting this guide, ensure you have:

  • ✅ A server with enough free storage
  • ✅ Domain name
  • ✅ SSL certificate (Let's Encrypt recommended)
  • ✅ Basic knowledge of Linux server administration

Architecture Overview

A typical production Chirp setup includes:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│ Nginx/Proxy │────│ Chirp App │────│ PostgreSQL │
│ (Port 80/443) │ │ (Port 3001) │ │ (Port 5432) │
└─────────────────┘ └─────────────────┘ └─────────────────┘

┌─────────────────┐
│ Redis │
│ (Port 6379) │
└─────────────────┘

Step 1: Server Preparation

System Update and Security

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install essential packages
sudo apt install -y curl wget git ufw fail2ban \
build-essential python3 software-properties-common \
certbot python3-certbot-nginx

# Configure firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable

Create Dedicated User

# Create chirp user
sudo useradd -m -s /bin/bash chirp
sudo usermod -aG sudo chirp

# Switch to chirp user
sudo su - chirp

Step 2: Install Dependencies

Node.js 18.x

# Add NodeSource repository
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -

# Install Node.js
sudo apt install -y nodejs

# Verify installation
node --version # Should be v18.x
npm --version # Should be 9.x

PostgreSQL 14+

# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib

# Start and enable PostgreSQL
sudo systemctl start postgresql
sudo systemctl enable postgresql

# Secure PostgreSQL
sudo -u postgres psql << EOF
-- Create database
CREATE DATABASE chirp_prod;

-- Create user with strong password
CREATE USER chirp_user WITH PASSWORD 'your_strong_password_here';

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE chirp_prod TO chirp_user;

-- Restrict user to their database
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT ALL ON SCHEMA public TO chirp_user;

\q
EOF

Redis

# Install Redis
sudo apt install -y redis-server

# Configure Redis for security
sudo nano /etc/redis/redis.conf

Update these settings in redis.conf:

# Bind to localhost only
bind 127.0.0.1 ::1

# Require password (optional but recommended)
requirepass your_redis_password

# Disable dangerous commands (optional but recommended)
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command KEYS ""
rename-command CONFIG ""
# Restart Redis
sudo systemctl restart redis
sudo systemctl enable redis

Step 3: Install Chirp

Clone and Setup

# Clone repository
git clone https://git.eidenz.moe/Eidenz/chirp.git /home/chirp/app
cd /home/chirp/app

# Install dependencies
npm ci --production

# Set ownership
sudo chown -R chirp:chirp /home/chirp/app

Configure Environment

# Create production environment file
cp server/.env.example server/.env
nano server/.env

Configure your production environment:

# Production settings
NODE_ENV=production
PORT=3001
BASE_URL=https://your-domain.com

# Database configuration
DB_HOST=localhost
DB_PORT=5432
DB_USER=chirp_user
DB_PASSWORD=your_strong_password_here
DB_NAME=chirp_prod
DB_SSL=false

# Security keys (generate new ones!)
JWT_SECRET=your_super_secure_jwt_secret_minimum_64_characters
ADMIN_KEY=your_secure_admin_key_for_api_access

# Redis configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password

# Email configuration (optional but recommended)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_SECURE=false
EMAIL_USER=your-email@gmail.com
EMAIL_PASSWORD=your_app_specific_password
EMAIL_FROM=Chirp <noreply@your-domain.com>

# File upload settings
MAX_FILE_SIZE=104857600
UPLOAD_PATH=/home/chirp/uploads

# Security settings
BCRYPT_ROUNDS=12
SESSION_SECRET=your_session_secret_here

# Optional: Tenor API for GIFs
TENOR_API_KEY=your_tenor_api_key

# Logging
LOG_LEVEL=info
LOG_FILE=/home/chirp/logs/chirp.log

Generate Secure Keys

# Generate JWT secret
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

# Generate session secret
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Step 4: Database Setup

Run Migrations

cd /home/chirp/app/server
npm run db:migrate

Create Admin User

# Create first admin user
npm run seed:admin

Or create manually:

node -e "
const bcrypt = require('bcrypt');
const { Pool } = require('pg');

const pool = new Pool({
host: 'localhost',
port: 5432,
user: 'chirp_user',
password: 'your_strong_password_here',
database: 'chirp_prod'
});

async function createAdmin() {
const hashedPassword = await bcrypt.hash('your_admin_password', 12);

await pool.query(\`
INSERT INTO users (username, email, password, is_admin, email_verified, created_at)
VALUES ('admin', 'admin@your-domain.com', \$1, true, true, NOW())
\`, [hashedPassword]);

console.log('Admin user created successfully');
await pool.end();
}

createAdmin().catch(console.error);
"

Step 5: Nginx Configuration

Install Nginx

sudo apt install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx

Configure Nginx

Create /etc/nginx/sites-available/chirp:

server {
listen 80;
server_name your-domain.com www.your-domain.com;

# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name your-domain.com www.your-domain.com;

# SSL Configuration
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

# SSL Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# Security Headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# File Upload Size
client_max_body_size 100M;

# Serve Static Files
location /uploads/ {
alias /home/chirp/uploads/;
expires 1y;
add_header Cache-Control "public, immutable";

# Security for uploads
location ~* \.(php|jsp|asp|sh|py|pl|rb)$ {
deny all;
}
}

# Proxy to Chirp App
location / {
proxy_pass http://127.0.0.1:3001;
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;

# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# WebSocket support for Socket.IO
location /socket.io/ {
proxy_pass http://127.0.0.1:3001;
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;
}

# Health check endpoint
location /health {
access_log off;
proxy_pass http://127.0.0.1:3001/api/health;
}
}

Enable Site

# Enable site
sudo ln -s /etc/nginx/sites-available/chirp /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

Step 6: SSL Certificate with Let's Encrypt

# Obtain SSL certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

Step 7: System Service

Create /etc/systemd/system/chirp.service:

[Unit]
Description=Chirp Social Platform
After=network.target postgresql.service nginx.service

[Service]
Type=simple
User=chirp
Group=chirp
WorkingDirectory=/home/chirp/app
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm start
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
SyslogIdentifier=chirp

# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/home/chirp/uploads /home/chirp/logs

# Resource limits
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# Reload systemd and enable service
sudo systemctl daemon-reload
sudo systemctl enable chirp
sudo systemctl start chirp

# Check status
sudo systemctl status chirp

Step 8: Monitoring and Logging

Log Rotation

Create /etc/logrotate.d/chirp:

/home/chirp/logs/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 644 chirp chirp
postrotate
systemctl reload chirp
endscript
}

Monitoring Setup

# Install monitoring tools
sudo apt install -y htop iotop nethogs

# Create monitoring script
cat > /home/chirp/monitor.sh << 'EOF'
#!/bin/bash

# Check if Chirp is running
if ! systemctl is-active --quiet chirp; then
echo "Chirp service is not running!" | mail -s "Chirp Alert" admin@your-domain.com
systemctl start chirp
fi

# Check disk space
DISK_USAGE=$(df /home/chirp | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 80 ]; then
echo "Disk usage is ${DISK_USAGE}%" | mail -s "Chirp Disk Alert" admin@your-domain.com
fi

# Check memory usage
MEM_USAGE=$(free | awk 'NR==2{printf "%.2f", $3*100/$2}')
if (( $(echo "$MEM_USAGE > 80" | bc -l) )); then
echo "Memory usage is ${MEM_USAGE}%" | mail -s "Chirp Memory Alert" admin@your-domain.com
fi
EOF

chmod +x /home/chirp/monitor.sh

# Add to crontab
echo "*/5 * * * * /home/chirp/monitor.sh" | crontab -

Step 9: Security Hardening

Fail2Ban Configuration

Create /etc/fail2ban/jail.d/chirp.conf:

[chirp-auth]
enabled = true
port = http,https
filter = chirp-auth
logpath = /home/chirp/logs/chirp.log
maxretry = 5
bantime = 3600
findtime = 600

[chirp-http]
enabled = true
port = http,https
filter = chirp-http
logpath = /var/log/nginx/access.log
maxretry = 20
bantime = 3600
findtime = 600

Create /etc/fail2ban/filter.d/chirp-auth.conf:

[Definition]
failregex = .*Failed login attempt.*IP: <HOST>
ignoreregex =
# Restart Fail2Ban
sudo systemctl restart fail2ban

Regular Updates

# Create update script
cat > /home/chirp/update.sh << 'EOF'
#!/bin/bash

cd /home/chirp/app
git fetch origin
LATEST=$(git rev-parse origin/main)
CURRENT=$(git rev-parse HEAD)

if [ "$LATEST" != "$CURRENT" ]; then
echo "Updating Chirp..."
git pull origin main
npm ci --production
cd server && npm run db:migrate
sudo systemctl restart chirp
echo "Chirp updated to version $LATEST"
fi
EOF

chmod +x /home/chirp/update.sh

# Add weekly update check
echo "0 2 * * 0 /home/chirp/update.sh" | sudo crontab -

Step 10: Backup Setup

Database Backup Script

Create /home/chirp/backup.sh:

#!/bin/bash

BACKUP_DIR="/home/chirp/backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="chirp_prod"
DB_USER="chirp_user"

# Create backup directory
mkdir -p $BACKUP_DIR

# Database backup
pg_dump -h localhost -U $DB_USER -d $DB_NAME | gzip > $BACKUP_DIR/db_backup_$DATE.sql.gz

# File backup
tar -czf $BACKUP_DIR/files_backup_$DATE.tar.gz /home/chirp/uploads

# Clean old backups (keep last 30 days)
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete

echo "Backup completed: $DATE"
chmod +x /home/chirp/backup.sh

# Add daily backup at 3 AM
echo "0 3 * * * /home/chirp/backup.sh" | crontab -

Final Verification

Health Checks

# Check service status
sudo systemctl status chirp
sudo systemctl status nginx
sudo systemctl status postgresql

# Check logs
sudo journalctl -u chirp -f

Test Installation

  1. Open https://your-domain.com in your browser
  2. Create an account
  3. Test basic functionality:
    • Create a server
    • Create a channel
    • Send a message
    • Upload a file

Production Checklist

  • System requirements met
  • Firewall configured (UFW)
  • SSL certificate installed
  • Nginx reverse proxy configured
  • Database secured with strong password
  • Environment variables configured
  • System service configured
  • Log rotation set up
  • Monitoring configured
  • Backup script created
  • Security hardening applied
  • Updates configured

Next Steps

Your Chirp instance is now running in production!