Skip to content

SSL Certificates

BadgerPanel requires HTTPS for production deployments. This guide covers three approaches to configuring SSL certificates: automatic certificates via Let's Encrypt, custom certificates, and self-signed certificates for development.

SSL Modes

The SSL_MODE environment variable in your .env file controls how certificates are configured:

ModeDescriptionUse Case
letsencryptAutomatic free certificates from Let's EncryptProduction (recommended)
customYour own certificate filesCorporate certificates, wildcard certs
selfsignedAuto-generated self-signed certificatesDevelopment and testing only

Let's Encrypt provides free, automatically-renewed TLS certificates. This is the recommended approach for production deployments.

Prerequisites

  • Your domain's DNS A record must point to your server's public IP
  • Port 80 must be open and accessible from the internet (used for domain validation)
  • Port 443 must be open for HTTPS traffic

Configuration

During setup or in your .env file:

bash
SSL_MODE=letsencrypt
DOMAIN=panel.your-domain.com
LETSENCRYPT_EMAIL=admin@your-domain.com

How It Works

  1. The Nginx container includes Certbot for Let's Encrypt integration
  2. On first startup, Certbot requests a certificate for your domain
  3. Let's Encrypt validates domain ownership via HTTP-01 challenge (port 80)
  4. The certificate is stored in the nginx/ssl/ directory
  5. Nginx is configured to use the certificate automatically
  6. Certificates are renewed automatically before expiration (every 60-90 days)

Verification

After starting the stack, verify the certificate:

bash
# Check certificate details
openssl s_client -connect panel.your-domain.com:443 -servername panel.your-domain.com </dev/null 2>/dev/null | openssl x509 -noout -dates -issuer

# Or use curl
curl -vI https://panel.your-domain.com 2>&1 | grep -E "issuer|expire"

You should see issuer: ... Let's Encrypt and a valid expiration date.

Troubleshooting Let's Encrypt

"Connection refused" or timeout during validation:

  • Ensure port 80 is open: ss -tlnp | grep :80
  • Check firewall rules: sudo ufw status or your cloud provider's security groups
  • Ensure no other service is using port 80

"DNS problem: NXDOMAIN":

  • DNS not yet propagated. Verify: dig panel.your-domain.com
  • Wait for propagation (can take up to 48 hours for new records)

Rate limit exceeded:

  • Let's Encrypt allows 5 duplicate certificates per week per domain
  • If hit, wait 7 days or use a different subdomain
  • Use the staging environment for testing: set LETSENCRYPT_STAGING=true in your .env
bash
# Check Certbot logs
docker compose logs nginx | grep -i certbot

Certificate Renewal

Certificates are renewed automatically. You can verify the renewal process:

bash
# Test renewal (dry run)
docker compose exec nginx certbot renew --dry-run

Renewal Notifications

Let's Encrypt sends renewal reminder emails to the address specified in LETSENCRYPT_EMAIL. Ensure this is a monitored mailbox.

Custom Certificates

Use custom certificates if you have certificates from a commercial CA, a corporate PKI, or a wildcard certificate.

Configuration

bash
SSL_MODE=custom
DOMAIN=panel.your-domain.com

Place Certificate Files

Copy your certificate files into the nginx/ssl/ directory:

bash
# Required files:
nginx/ssl/cert.pem        # Your certificate (and intermediate chain)
nginx/ssl/privkey.pem      # Your private key

The certificate file should contain the full chain (your certificate followed by intermediate certificates):

-----BEGIN CERTIFICATE-----
(Your domain certificate)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Intermediate certificate)
-----END CERTIFICATE-----

File Permissions

Ensure proper permissions on the private key:

bash
chmod 600 nginx/ssl/privkey.pem
chmod 644 nginx/ssl/cert.pem

Restart Nginx

bash
docker compose restart nginx

Verify

bash
curl -vI https://panel.your-domain.com 2>&1 | grep -E "issuer|expire|subject"

Certificate Renewal

With custom certificates, you are responsible for renewal. When you receive a renewed certificate:

bash
# Replace the certificate files
cp /path/to/new/cert.pem nginx/ssl/cert.pem
cp /path/to/new/privkey.pem nginx/ssl/privkey.pem

# Reload Nginx without downtime
docker compose exec nginx nginx -s reload

Expiration Monitoring

Set a calendar reminder to renew your certificates before they expire. Expired certificates will prevent users from accessing the panel.

Self-Signed Certificates (Development Only)

Self-signed certificates are generated automatically when using selfsigned mode. They are intended for local development and testing only.

Configuration

bash
SSL_MODE=selfsigned
DOMAIN=panel.your-domain.com

How It Works

The setup script generates a self-signed certificate:

bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout nginx/ssl/privkey.pem \
  -out nginx/ssl/cert.pem \
  -subj "/CN=panel.your-domain.com"

Browser Warnings

Browsers will display a security warning for self-signed certificates. This is expected:

  • Chrome: Click "Advanced" then "Proceed to ... (unsafe)"
  • Firefox: Click "Advanced" then "Accept the Risk and Continue"
  • Safari: Click "Show Details" then "visit this website"

Not for Production

Self-signed certificates provide encryption but no identity verification. They will:

  • Show security warnings to all users
  • Prevent API clients from connecting without disabling certificate verification
  • Be rejected by BadgerDaemon and BadgerOrchestrator by default

Never use self-signed certificates in production.

Nginx TLS Configuration

BadgerPanel's Nginx configuration includes modern TLS settings by default:

nginx
# TLS protocols (TLS 1.2 and 1.3 only)
ssl_protocols TLSv1.2 TLSv1.3;

# Cipher suites (modern, secure)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# HSTS (1 year, includeSubDomains)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;

# Session caching
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

Testing TLS Configuration

Use SSL Labs to test your TLS configuration:

https://www.ssllabs.com/ssltest/analyze.html?d=panel.your-domain.com

A properly configured BadgerPanel instance should receive an A or A+ rating.

Cloudflare Integration

If you use Cloudflare as your DNS provider and CDN:

Cloudflare SSL Settings

  1. In Cloudflare dashboard, go to SSL/TLS > Overview
  2. Set SSL mode to Full (strict) (requires a valid certificate on your server)

WebSocket Support

Ensure WebSocket support is enabled (required for console streaming):

  1. Go to Network in Cloudflare dashboard
  2. Verify WebSockets is enabled
SettingValueReason
SSL ModeFull (strict)End-to-end encryption
Minimum TLS1.2Security
Always Use HTTPSOnRedirect HTTP traffic
WebSocketsOnConsole and real-time features
HTTP/2OnPerformance

Cloudflare Proxy

If using Cloudflare's proxy (orange cloud), game server traffic (TCP/UDP on non-standard ports) cannot pass through Cloudflare. Only the panel web interface benefits from Cloudflare's proxy. Game server nodes should use DNS-only (grey cloud) records.

Next Steps

BadgerPanel Documentation