Nginx & TLS Variables
SSL certificate and nginx web server configuration.
Overview
Nginx acts as a reverse proxy for all Chatty AI services, providing:
- HTTPS termination
- SSL certificate management
- Request routing to backend services
- Load balancing
Certificate Directory Structure
Portainer Deployment
Certificates are organized in subdirectories:
/etc/nginx/certs/
├── chattyai/
│ ├── fullchain.pem # ChattyAI certificate
│ └── privkey.pem # ChattyAI private key
├── n8n/
│ ├── fullchain.pem # n8n certificate
│ └── privkey.pem # n8n private key
└── databases/
├── fullchain.pem # Databases certificate
└── privkey.pem # Databases private key
Docker Compose Deployment
Certificates relative to ./deploy/certs/:
deploy/certs/
├── chattyai/
│ ├── fullchain.pem
│ └── privkey.pem
├── n8n/
│ ├── fullchain.pem
│ └── privkey.pem
└── databases/
├── fullchain.pem
└── privkey.pem
SSL Certificate Variables
Chatty AI Certificates
CHATTYAI_SSL_CERT
- Type: String (file path)
- Required: No
- Default:
- Portainer:
/etc/nginx/certs/chattyai/fullchain.pem - Docker Compose:
chattyai/fullchain.pem
- Portainer:
- Example:
/etc/nginx/certs/chattyai/my-cert.pem - Description: Full path to SSL certificate chain for Chatty AI
- Format: PEM format, includes full certificate chain
- Used in: nginx service
CHATTYAI_SSL_KEY
- Type: String (file path)
- Required: No
- Default:
- Portainer:
/etc/nginx/certs/chattyai/privkey.pem - Docker Compose:
chattyai/privkey.pem
- Portainer:
- Example:
/etc/nginx/certs/chattyai/my-key.pem - Description: Full path to SSL private key for Chatty AI
- Security: 🔴 CRITICAL - Keep private key secure
- Permissions: Should be readable only by nginx container
- Used in: nginx service
n8n Certificates
N8N_SSL_CERT
- Type: String (file path)
- Required: No
- Default:
- Portainer:
/etc/nginx/certs/n8n/fullchain.pem - Docker Compose:
n8n/fullchain.pem
- Portainer:
- Example:
/etc/nginx/certs/n8n/n8n-cert.pem - Description: Full path to SSL certificate chain for n8n
- Format: PEM format
- Used in: nginx service
N8N_SSL_KEY
- Type: String (file path)
- Required: No
- Default:
- Portainer:
/etc/nginx/certs/n8n/privkey.pem - Docker Compose:
n8n/privkey.pem
- Portainer:
- Example:
/etc/nginx/certs/n8n/n8n-key.pem - Description: Full path to SSL private key for n8n
- Security: 🔴 CRITICAL - Keep private key secure
- Used in: nginx service
Databases Certificates
DATABASES_SSL_CERT
- Type: String (file path)
- Required: No
- Default:
- Portainer:
/etc/nginx/certs/databases/fullchain.pem - Docker Compose:
databases/fullchain.pem
- Portainer:
- Example:
/etc/nginx/certs/databases/db-cert.pem - Description: Full path to SSL certificate chain for Databases UI
- Format: PEM format
- Used in: nginx service
DATABASES_SSL_KEY
- Type: String (file path)
- Required: No
- Default:
- Portainer:
/etc/nginx/certs/databases/privkey.pem - Docker Compose:
databases/privkey.pem
- Portainer:
- Example:
/etc/nginx/certs/databases/db-key.pem - Description: Full path to SSL private key for Databases UI
- Security: 🔴 CRITICAL - Keep private key secure
- Used in: nginx service
Certificate Setup
Option 1: Use Default Paths (Recommended)
Just place certificates in the expected structure:
Portainer:
# Mount certificates
-v /path/to/certs:/etc/nginx/certs:ro
# Structure
/path/to/certs/
├── chattyai/fullchain.pem & privkey.pem
├── n8n/fullchain.pem & privkey.pem
└── databases/fullchain.pem & privkey.pem
Docker Compose:
# Place in deploy/certs/
deploy/certs/
├── chattyai/fullchain.pem & privkey.pem
├── n8n/fullchain.pem & privkey.pem
└── databases/fullchain.pem & privkey.pem
Option 2: Custom Paths
Set custom paths in .env:
# Custom certificate locations
CHATTYAI_SSL_CERT=/custom/path/chatty-cert.pem
CHATTYAI_SSL_KEY=/custom/path/chatty-key.pem
N8N_SSL_CERT=/custom/path/n8n-cert.pem
N8N_SSL_KEY=/custom/path/n8n-key.pem
DATABASES_SSL_CERT=/custom/path/db-cert.pem
DATABASES_SSL_KEY=/custom/path/db-key.pem
Certificate Types
Let's Encrypt (Recommended)
Free, automated certificates:
# Install certbot
sudo apt-get install certbot
# Get certificate
sudo certbot certonly --standalone -d chat.example.com
# Copy to Chatty AI
sudo cp /etc/letsencrypt/live/chat.example.com/fullchain.pem /path/to/certs/chattyai/
sudo cp /etc/letsencrypt/live/chat.example.com/privkey.pem /path/to/certs/chattyai/
Wildcard Certificates
One certificate for all subdomains:
# Get wildcard cert
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com"
# Use same cert for all services
cp /etc/letsencrypt/live/example.com/fullchain.pem /path/to/certs/chattyai/
cp /etc/letsencrypt/live/example.com/privkey.pem /path/to/certs/chattyai/
cp /etc/letsencrypt/live/example.com/fullchain.pem /path/to/certs/n8n/
cp /etc/letsencrypt/live/example.com/privkey.pem /path/to/certs/n8n/
cp /etc/letsencrypt/live/example.com/fullchain.pem /path/to/certs/databases/
cp /etc/letsencrypt/live/example.com/privkey.pem /path/to/certs/databases/
Commercial Certificates
From providers like DigiCert, Sectigo, etc.:
# You'll receive:
# - certificate.crt (your certificate)
# - intermediate.crt (intermediate CA)
# - root.crt (root CA)
# Create fullchain
cat certificate.crt intermediate.crt root.crt > fullchain.pem
# Copy to Chatty AI
cp fullchain.pem /path/to/certs/chattyai/
cp private.key /path/to/certs/chattyai/privkey.pem
Self-Signed (Testing Only)
⚠️ Not for production:
# Generate self-signed certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout privkey.pem \
-out fullchain.pem \
-subj "/CN=chat.example.com"
# Copy to Chatty AI
cp fullchain.pem /path/to/certs/chattyai/
cp privkey.pem /path/to/certs/chattyai/
Certificate Renewal
Let's Encrypt Auto-Renewal
# Test renewal
sudo certbot renew --dry-run
# Setup auto-renewal (runs twice daily)
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
# After renewal, copy new certificates
sudo cp /etc/letsencrypt/live/chat.example.com/fullchain.pem /path/to/certs/chattyai/
sudo cp /etc/letsencrypt/live/chat.example.com/privkey.pem /path/to/certs/chattyai/
# Restart nginx
docker compose restart nginx
Manual Renewal
# 1. Get new certificates
# 2. Copy to certificate directory
# 3. Restart nginx
docker compose restart nginx
Certificate Permissions
Correct Permissions
# Certificate (public) - readable by all
chmod 644 /path/to/certs/*/fullchain.pem
# Private key - readable only by owner
chmod 600 /path/to/certs/*/privkey.pem
# Verify
ls -la /path/to/certs/chattyai/
# -rw-r--r-- fullchain.pem
# -rw------- privkey.pem
Troubleshooting
Nginx Won't Start
Check certificate paths:
# View nginx logs
docker compose logs nginx
# Common error:
# "SSL: error:02001002:system library:fopen:No such file or directory"
Verify files exist:
ls -la /path/to/certs/chattyai/fullchain.pem
ls -la /path/to/certs/chattyai/privkey.pem
Certificate Expired
Check expiration:
openssl x509 -in /path/to/certs/chattyai/fullchain.pem -noout -dates
Renew and restart:
# Renew certificate
sudo certbot renew
# Copy new certificate
sudo cp /etc/letsencrypt/live/chat.example.com/* /path/to/certs/chattyai/
# Restart nginx
docker compose restart nginx
Browser Shows "Not Secure"
Check certificate chain:
openssl x509 -in /path/to/certs/chattyai/fullchain.pem -text -noout
Verify it includes intermediate certificates:
# Should show multiple certificates
openssl crl2pkcs7 -nocrl -certfile /path/to/certs/chattyai/fullchain.pem | openssl pkcs7 -print_certs -noout
HTTP Instead of HTTPS
If certificates not found, nginx runs in HTTP mode.
Check logs:
docker compose logs nginx | grep "WARNING: SSL certificates not found"
Mount certificates correctly:
# Portainer
-v /path/to/certs:/etc/nginx/certs:ro
# Docker Compose (already mounted in docker-compose.yaml)
Security Best Practices
1. Protect Private Keys
# Never commit to git
echo "*.pem" >> .gitignore
echo "*.key" >> .gitignore
# Restrict permissions
chmod 600 /path/to/certs/*/privkey.pem
# Backup securely
tar czf certs-backup.tar.gz /path/to/certs/
gpg -c certs-backup.tar.gz
2. Use Strong Certificates
- Minimum 2048-bit RSA (4096-bit recommended)
- Use SHA-256 or better
- Include full certificate chain
- Use trusted CA (not self-signed in production)
3. Monitor Expiration
# Check expiration dates
for cert in /path/to/certs/*/fullchain.pem; do
echo "=== $cert ==="
openssl x509 -in "$cert" -noout -dates
done
# Set up monitoring
# Alert 30 days before expiration
4. Rotate Regularly
- Let's Encrypt: Auto-renews every 60 days
- Commercial: Renew before expiration
- Update all services after renewal
Related Documentation
- Core Application Variables - Domain configuration
- Installation - Certificate setup during installation
- Service Management - Managing nginx service