Production
This guide covers deploying Azu applications to production environments with proper configuration, monitoring, and security considerations.
Deployment Architecture
Production Checklist
Pre-deployment
Security Configuration
# Production security settings
Azu.configure do |config|
config.debug = false
config.ssl = true
config.ssl_cert = "/etc/ssl/certs/app.crt"
config.ssl_key = "/etc/ssl/private/app.key"
# Security headers
config.security_headers = {
"Strict-Transport-Security" => "max-age=31536000; includeSubDomains",
"X-Content-Type-Options" => "nosniff",
"X-Frame-Options" => "DENY",
"X-XSS-Protection" => "1; mode=block"
}
endEnvironment Variables
# Production environment variables
export AZU_ENVIRONMENT=production
export AZU_PORT=443
export AZU_HOST=0.0.0.0
export AZU_DEBUG=false
export AZU_SSL=true
export AZU_SSL_CERT=/etc/ssl/certs/app.crt
export AZU_SSL_KEY=/etc/ssl/private/app.key
export DATABASE_URL=postgresql://user:pass@localhost:5432/app_prod
export REDIS_URL=redis://localhost:6379/0
export SECRET_KEY=your-production-secret-keyServer Configuration
Nginx Reverse Proxy
# /etc/nginx/sites-available/azu-app
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/ssl/certs/app.crt;
ssl_certificate_key /etc/ssl/private/app.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256;
location / {
proxy_pass http://127.0.0.1:3000;
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;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static files
location /static {
alias /var/www/azu-app/public;
expires 1y;
add_header Cache-Control "public, immutable";
}
}Apache Virtual Host
# /etc/apache2/sites-available/azu-app.conf
<VirtualHost *:80>
ServerName your-domain.com
Redirect permanent / https://your-domain.com/
</VirtualHost>
<VirtualHost *:443>
ServerName your-domain.com
DocumentRoot /var/www/azu-app/public
SSLEngine on
SSLCertificateFile /etc/ssl/certs/app.crt
SSLCertificateKeyFile /etc/ssl/private/app.key
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:3000/$1" [P,L]
</VirtualHost>Process Management
Systemd Service
# /etc/systemd/system/azu-app.service
[Unit]
Description=Azu Web Application
After=network.target
[Service]
Type=simple
User=azu
Group=azu
WorkingDirectory=/var/www/azu-app
ExecStart=/usr/local/bin/azu-app
Restart=always
RestartSec=5
Environment=AZU_ENVIRONMENT=production
Environment=DATABASE_URL=postgresql://user:pass@localhost:5432/app_prod
Environment=REDIS_URL=redis://localhost:6379/0
[Install]
WantedBy=multi-user.targetPM2 Process Manager
// ecosystem.config.js
module.exports = {
apps: [
{
name: "azu-app",
script: "./bin/azu-app",
instances: "max",
exec_mode: "cluster",
env: {
NODE_ENV: "production",
AZU_ENVIRONMENT: "production",
PORT: 3000,
},
error_file: "./logs/err.log",
out_file: "./logs/out.log",
log_file: "./logs/combined.log",
time: true,
},
],
};Docker Compose
# docker-compose.prod.yml
version: "3.8"
services:
app:
build: .
ports:
- "3000:3000"
environment:
- AZU_ENVIRONMENT=production
- DATABASE_URL=postgresql://user:pass@db:5432/app_prod
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15
environment:
- POSTGRES_DB=app_prod
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/ssl
depends_on:
- app
restart: unless-stopped
volumes:
postgres_data:
redis_data:Database Setup
PostgreSQL Configuration
-- Create production database
CREATE DATABASE app_prod;
CREATE USER app_user WITH PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE app_prod TO app_user;
-- Create read replica user
CREATE USER app_readonly WITH PASSWORD 'readonly_password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;Database Migrations
# Run migrations in production
Azu::Database.migrate
# Or with specific version
Azu::Database.migrate_to_version(20231201000000)Database Backup
# Automated backup script
#!/bin/bash
BACKUP_DIR="/var/backups/azu-app"
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump app_prod > "$BACKUP_DIR/backup_$DATE.sql"
gzip "$BACKUP_DIR/backup_$DATE.sql"
# Keep only last 30 days
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -deleteMonitoring and Logging
Application Monitoring
# Production monitoring configuration
Azu.configure do |config|
config.monitoring_enabled = true
config.metrics_port = 9090
config.health_check_path = "/health"
# Logging
config.log_level = :info
config.log_format = :json
config.log_file = "/var/log/azu-app/app.log"
endHealth Check Endpoint
struct HealthEndpoint
include Azu::Endpoint
get "/health"
def call
health_status = {
"status" => "healthy",
"timestamp" => Time.utc.to_s,
"version" => Azu::VERSION,
"database" => check_database,
"redis" => check_redis,
"memory" => check_memory
}
response.header("Content-Type", "application/json")
response.body(health_status.to_json)
end
private def check_database
# Database health check
true
end
private def check_redis
# Redis health check
true
end
private def check_memory
# Memory usage check
System.memory_usage
end
endLog Aggregation
# Structured logging for production
Azu::Logger.configure do |config|
config.level = :info
config.format = :json
config.output = STDOUT
end
# Custom log formatter
class ProductionLogger < Azu::Logger
def format(level, message, context)
{
timestamp: Time.utc.to_s,
level: level.to_s,
message: message,
context: context,
service: "azu-app",
version: Azu::VERSION
}.to_json
end
endPerformance Optimization
Caching Strategy
# Production caching configuration
Azu.configure do |config|
config.cache_type = :redis
config.cache_ttl = 1.hour
config.cache_compression = true
# Cache warming
config.cache_warmup = true
config.cache_warmup_paths = ["/api/users", "/api/posts"]
endDatabase Optimization
# Database connection pooling
Azu.configure do |config|
config.database_pool_size = 20
config.database_pool_timeout = 5.seconds
config.database_statement_timeout = 30.seconds
endStatic File Serving
# Static file optimization
Azu.configure do |config|
config.static_cache_ttl = 1.year
config.static_compression = true
config.static_etag = true
endSecurity Hardening
SSL/TLS Configuration
# SSL configuration
Azu.configure do |config|
config.ssl = true
config.ssl_cert = "/etc/ssl/certs/app.crt"
config.ssl_key = "/etc/ssl/private/app.key"
config.ssl_protocols = ["TLSv1.2", "TLSv1.3"]
config.ssl_ciphers = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
endSecurity Headers
# Security headers middleware
class SecurityHeaders < Azu::Handler::Base
def call(request, response)
response.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
response.header("X-Content-Type-Options", "nosniff")
response.header("X-Frame-Options", "DENY")
response.header("X-XSS-Protection", "1; mode=block")
response.header("Referrer-Policy", "strict-origin-when-cross-origin")
response.header("Content-Security-Policy", "default-src 'self'")
yield
end
endRate Limiting
# Production rate limiting
Azu.configure do |config|
config.rate_limit_requests = 1000
config.rate_limit_window = 1.minute
config.rate_limit_storage = :redis
config.rate_limit_skip_successful = true
endBackup and Recovery
Database Backup
# Automated database backup
#!/bin/bash
BACKUP_DIR="/var/backups/azu-app"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup
pg_dump app_prod | gzip > "$BACKUP_DIR/db_backup_$DATE.sql.gz"
# Upload to cloud storage
aws s3 cp "$BACKUP_DIR/db_backup_$DATE.sql.gz" s3://azu-app-backups/
# Cleanup old backups
find $BACKUP_DIR -name "db_backup_*.sql.gz" -mtime +7 -deleteApplication Backup
# Application files backup
#!/bin/bash
BACKUP_DIR="/var/backups/azu-app"
APP_DIR="/var/www/azu-app"
DATE=$(date +%Y%m%d_%H%M%S)
# Create application backup
tar -czf "$BACKUP_DIR/app_backup_$DATE.tar.gz" -C $APP_DIR .
# Upload to cloud storage
aws s3 cp "$BACKUP_DIR/app_backup_$DATE.tar.gz" s3://azu-app-backups/Deployment Automation
CI/CD Pipeline
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Crystal
uses: oprypin/crystal-setup-action@v1
with:
crystal-version: "1.15.1"
- name: Install dependencies
run: shards install
- name: Run tests
run: crystal spec
- name: Build application
run: crystal build --release src/azu-app.cr
- name: Deploy to production
run: |
# Deploy script
./scripts/deploy.shDeployment Script
#!/bin/bash
# scripts/deploy.sh
set -e
echo "Starting deployment..."
# Pull latest code
git pull origin main
# Install dependencies
shards install
# Run migrations
crystal run src/migrate.cr
# Build application
crystal build --release src/azu-app.cr
# Restart services
sudo systemctl restart azu-app
sudo systemctl reload nginx
echo "Deployment completed successfully!"Troubleshooting
Common Issues
Port already in use
sudo lsof -i :3000 sudo kill -9 <PID>Database connection issues
# Check database status sudo systemctl status postgresql # Check connection psql -h localhost -U app_user -d app_prodSSL certificate issues
# Check certificate validity openssl x509 -in /etc/ssl/certs/app.crt -text -noout # Test SSL connection openssl s_client -connect your-domain.com:443
Performance Issues
High CPU usage
Check for infinite loops
Monitor database queries
Review caching strategy
Memory leaks
Monitor memory usage
Check for unclosed connections
Review object lifecycle
Slow response times
Enable query logging
Check database indexes
Review caching configuration
Next Steps
Learn about Docker Deployment
Explore Scaling Strategies
Understand Monitoring and Alerting
Last updated
Was this helpful?
