HTTPS Setup Guide
This guide explains how to configure HTTPS for EAS Station using nginx and Let's Encrypt.
Table of Contents
- Overview
- Architecture
- Prerequisites
- Quick Start
- Configuration Options
- Troubleshooting
- Advanced Topics
Overview
EAS Station includes built-in HTTPS support using:
- nginx - Reverse proxy server that handles SSL/TLS termination
- Let's Encrypt - Free, automated SSL certificate authority
- certbot - Automated certificate management and renewal
What This Provides
✅ Automatic HTTPS - Certificates are automatically obtained and renewed ✅ Secure by Default - Modern TLS configuration (TLS 1.2+, strong ciphers) ✅ HTTP → HTTPS Redirect - All HTTP traffic is automatically redirected to HTTPS ✅ Self-Signed Fallback - Generates self-signed certificates for development/testing ✅ Zero Configuration - Works out of the box for localhost testing
Architecture
Internet (HTTPS) → nginx (ports 80/443) → Flask App (internal port 5000)
↑
Let's Encrypt certificates
(auto-renewed by certbot)
How it works:- nginx listens on ports 80 (HTTP) and 443 (HTTPS)
- Port 80 serves Let's Encrypt ACME challenges and redirects to HTTPS
- Port 443 terminates SSL and proxies requests to Flask on internal port 5000
- certbot runs every 12 hours to check and renew certificates (90-day validity)
- Flask application only needs to handle HTTP internally
Prerequisites
For Production Deployment (Let's Encrypt):
- ✅ Domain name - You must own a domain (e.g.,
eas.example.com) - ✅ DNS configured - Domain must point to your server's public IP address
- ✅ Port 80 accessible - Must be reachable from the internet for ACME validation
- ✅ Port 443 accessible - For HTTPS traffic
- ✅ Valid email - For Let's Encrypt certificate expiration notifications
For Development/Testing:
- ✅ Just Docker and Docker Compose
- ⚠️ Will use self-signed certificates (browser warnings expected)
Quick Start
Option 1: Local Testing (Self-Signed Certificate)
No configuration needed! Just deploy:Using main docker-compose.yml
docker compose up -dOr using embedded database
docker compose -f docker-compose.embedded-db.yml up -d
Access your application at:
- https://localhost (⚠️ browser will show security warning - this is expected)
- HTTP traffic automatically redirects to HTTPS
- Click "Advanced" → "Proceed to localhost (unsafe)"
- This is safe for local testing
- Self-signed certificates are not trusted by browsers
Option 2: Production Deployment (Let's Encrypt)
Before deploying, configure your environment variables:Step 1: Configure Environment Variables
Edit stack.env or .env:
Your domain name (REQUIRED)
DOMAIN_NAME=eas.example.comEmail for certificate notifications (REQUIRED)
SSL_EMAIL=admin@example.comUse production Let's Encrypt server
CERTBOT_STAGING=0
Step 2: Verify DNS
Ensure your domain points to your server:
Check DNS resolution
nslookup eas.example.comShould return your server's public IP
Step 3: Deploy
docker compose up -d
Step 4: Verify Certificate
Check the logs:
docker compose logs nginx
docker compose logs certbot
Look for:
Successfully obtained SSL certificate
Access your application:
- https://eas.example.com (✅ secure, no warnings)
- HTTP automatically redirects to HTTPS
Configuration Options
Environment Variables
| Variable | Description | Default | Required |
|---|---|---|---|
DOMAIN_NAME |
Domain name for SSL certificate | localhost |
Yes (for production) |
SSL_EMAIL |
Email for Let's Encrypt notifications | admin@example.com |
Yes (for production) |
CERTBOT_STAGING |
Use Let's Encrypt staging server | 0 |
No |
Let's Encrypt Staging Mode
When to use staging mode:CERTBOT_STAGING=1
✅ Use staging when:
- Testing certificate configuration
- Developing SSL features
- Avoiding production rate limits (50 certificates/week per domain)
⚠️ Staging certificates:
- Not trusted by browsers
- Show security warnings
- Good for testing automation
❌ Don't use staging for:
- Production deployments
- Public-facing sites
Rate Limits
Let's Encrypt production limits:
- 50 certificates per registered domain per week
- 5 failed validation attempts per hour
- Test with
CERTBOT_STAGING=1first - Switch to production only when configuration is confirmed working
- Don't delete and recreate certificates unnecessarily
Troubleshooting
Problem: Certificate Not Obtained
Symptom: Self-signed certificate generated instead of Let's EncryptCheck these:- DNS Resolution
nslookup $DOMAIN_NAME
Should return your server's IP- Port 80 Accessibility
curl http://$DOMAIN_NAME/.well-known/acme-challenge/test
Should connect (404 is OK, connection timeout is bad)- Firewall Rules
# Check if port 80 is listening
docker compose ps # Test from external host
curl -I http://your-ip-address
- Check Logs
docker compose logs nginx
docker compose logs certbot
Common causes:
- Domain not pointing to server
- Firewall blocking port 80
- Another service using port 80
- Domain validation timeout
Problem: Browser Shows "Not Secure"
Possible causes:- Self-signed certificate (expected for localhost)
- Solution: Use a real domain name with Let's Encrypt
- Certificate not installed yet
- Check:
docker compose logs nginx - Solution: Wait for initialization to complete
- Mixed content (HTTPS page loading HTTP resources)
- Check browser console for errors
- Solution: Ensure all resources use HTTPS or relative URLs
Problem: Certificate Renewal Failed
Check:docker compose logs certbot
Common causes:
- Port 80 blocked
- Domain DNS changed
- Disk full (can't write renewed certificate)
Test renewal (dry run)
docker compose exec certbot certbot renew --dry-runForce renewal
docker compose exec certbot certbot renew --force-renewal
Problem: Port 80 Already in Use
Symptom:Error: port 80 is already allocated
Find what's using the port:sudo lsof -i :80
or
sudo netstat -tlnp | grep :80
Solutions:- Stop conflicting service:
sudo systemctl stop apache2 # or nginx, etc.
sudo systemctl disable apache2
- Use different port mapping:
docker-compose.yml:
ports:
- "0.0.0.0:8080:80" # Use port 8080 for IPv4 clients
- "[::]:8080:80" # Use port 8080 for IPv6 clients
- "0.0.0.0:8443:443" # Use port 8443 for IPv4 clients
- "[::]:8443:443" # Use port 8443 for IPv6 clients
⚠️ Note: Let's Encrypt requires port 80 for validation
Advanced Topics
Custom nginx Configuration
The default nginx configuration is in nginx.conf. To customize:
- Edit
nginx.conf - Rebuild and restart:
docker compose restart nginx
Common customizations:Increase Upload Size Limit
In nginx.conf server block
clientmaxbody_size 500M; # Default is 100M
Add Custom Headers
In nginx.conf server block
add_header X-Custom-Header "value" always;
Adjust Rate Limiting
At top of nginx.conf
limitreqzone $binaryremoteaddr zone=api_limit:10m rate=100r/s; # More permissive
Multiple Domains
To serve multiple domains:
- Edit nginx.conf:
server {
listen 443 ssl http2;
server_name eas1.example.com eas2.example.com;
# ... rest of config
}
- Obtain certificates for all domains:
DOMAIN_NAME="eas1.example.com eas2.example.com" docker compose up -d
Using Existing Certificates
If you already have SSL certificates:
- Create certificate directories:
mkdir -p certs/live/yourdomain.com
- Copy your certificates:
cp fullchain.pem certs/live/yourdomain.com/
cp privkey.pem certs/live/yourdomain.com/
cp chain.pem certs/live/yourdomain.com/
- Mount certificates in docker-compose.yml:
volumes:
- ./certs:/etc/letsencrypt:ro
Monitoring Certificate Expiration
Check certificate expiration:
View certificate details
docker compose exec nginx openssl sclient -connect localhost:443 -servername $DOMAINNAME < /dev/null 2>/dev/null | openssl x509 -noout -datesOutput shows:
notBefore=...
notAfter=...
Automatic renewal:
- certbot checks every 12 hours
- Renews certificates 30 days before expiration
- No manual intervention required
HTTPS-Only Mode
To completely disable HTTP (port 80):
- Edit nginx.conf - Remove HTTP server block
- Edit docker-compose.yml:
nginx:
ports:
- "443:443" # Remove port 80 mapping
⚠️ Warning: This breaks Let's Encrypt ACME challenges. Only use if you have other certificate management.
SSL/TLS Configuration
Current configuration:
- Protocols: TLS 1.2, TLS 1.3
- Ciphers: Mozilla Intermediate compatibility
- HSTS: Enabled (63072000 seconds = 2 years)
- OCSP Stapling: Enabled
To modify, edit nginx.conf:
ssl_protocols TLSv1.3; # TLS 1.3 only (more restrictive)
ssl_ciphers 'HIGH:!aNULL:!MD5'; # Different cipher suite
Test your configuration:
- https://www.ssllabs.com/ssltest/
- https://observatory.mozilla.org/
Security Best Practices
- Always use production certificates for public deployments
- Keep certbot updated - Done automatically with
certbot/certbot:latest - Monitor expiration - certbot sends emails 30/14/7 days before expiration
- Protect private keys - Never commit certificate files to git
- Use strong ciphers - Default configuration uses Mozilla Intermediate
- Enable HSTS - Already enabled in default config
- Regular updates - Update Docker images monthly
Related Documentation
- SYSTEM_DEPENDENCIES.md - Infrastructure components
- dependency_attribution.md - nginx and certbot attribution
- PORTAINER_DEPLOYMENT.md - Deployment via Portainer
- SECURITY.md - General security guidelines
Support
Certificate not working?- Check troubleshooting section above
- Review nginx and certbot logs
- Verify DNS and firewall configuration
- Review this documentation
- Check project issues: https://github.com/KR8MER/eas-station/issues
Last Updated: 2025-11-09 Author: KR8MER Amateur Radio Emergency Communications
This document is served from docs/guides/HTTPS_SETUP.md in the EAS Station installation.