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
- Open https://your-domain.com in your browser
- Create an account
- 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!