Skip to main content

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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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

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

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