7.5 KiB
Traefik SSL Configuration for MerchBay Admin
This document explains how to configure paid SSL certificates for production and automatic Let's Encrypt for development.
Network Configuration
All deployments use the external network: traefik-public
# Ensure the network exists
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
Development (dev.merchbay.app) - Automatic SSL
Development uses Let's Encrypt for automatic SSL certificate generation.
Traefik Configuration
Ensure your Traefik has Let's Encrypt configured:
# traefik.yml or dynamic config
certificatesResolvers:
letsencrypt:
acme:
email: admin@merchbay.app
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
Application Labels (Already configured in docker-compose)
labels:
- "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt"
Production (merchbay.app) - Paid SSL Certificate
Production uses a paid SSL certificate (e.g., from GoDaddy, Namecheap, Cloudflare).
Step 1: Prepare SSL Certificate Files
You should have these files from your SSL provider:
merchbay.app.crt- Certificate filemerchbay.app.key- Private key fileca-bundle.crt- CA bundle (optional, for chain)
Create a combined certificate file:
# Your SSL certificates are in /srv/certs
# Verify files exist
ls -la /srv/certs/
# If you have a CA bundle, create a full chain
cd /srv/certs
cat merchbay.app.crt ca-bundle.crt > merchbay.app-fullchain.crt
# Set proper permissions
sudo chmod 600 /srv/certs/*.key
sudo chmod 644 /srv/certs/*.crt
Step 2: Configure Traefik File Provider
Create a dynamic configuration file for Traefik:
sudo nano /opt/traefik/dynamic/certs.yml
Add:
# /opt/traefik/dynamic/certs.yml or your Traefik dynamic config location
tls:
certificates:
- certFile: /srv/certs/merchbay.app-fullchain.crt
keyFile: /srv/certs/merchbay.app.key
stores:
- default
stores:
default:
defaultCertificate:
certFile: /srv/certs/merchbay.app-fullchain.crt
keyFile: /srv/certs/merchbay.app.key
Step 3: Update Traefik docker-compose.yml
Ensure Traefik has file provider enabled and certificates mounted:
services:
traefik:
image: traefik:v2.10
command:
- "--providers.docker=true"
- "--providers.docker.network=traefik-public"
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# Let's Encrypt for dev
- "--certificatesresolvers.letsencrypt.acme.email=admin@merchbay.app"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /srv/certs:/srv/certs:ro
- /opt/traefik/dynamic:/etc/traefik/dynamic:ro
- traefik-letsencrypt:/letsencrypt
networks:
- traefik-public
Step 4: Restart Traefik
cd /path/to/traefik
docker compose restart traefik
# Verify certificates are loaded
docker compose logs traefik | grep -i cert
Application Configuration
Development Branch (dev)
File: docker-compose.portainer.dev.yml
- Domain:
dev.merchbay.app - SSL: Let's Encrypt (automatic)
- Certificate Resolver:
letsencrypt
labels:
- "traefik.http.routers.merchbay-admin-dev.rule=Host(`dev.merchbay.app`)"
- "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt"
Production Branch (main)
File: docker-compose.portainer.yml
- Domain:
merchbay.app - SSL: Paid certificate (via Traefik file provider)
- No certresolver label (uses default store)
labels:
- "traefik.http.routers.merchbay-admin.rule=Host(`merchbay.app`)"
- "traefik.http.routers.merchbay-admin.tls=true"
# No certresolver - uses file provider certificate
Gitea Secrets Configuration
Development Secrets
DEV_DB_HOST=dev-mysql-host
DEV_DB_PORT=3306
DEV_DB_DATABASE=merchbay_admin_dev
DEV_DB_USERNAME=dev_user
DEV_DB_PASSWORD=dev_password
Production Secrets
PROD_DEPLOY_HOST=prod-server-ip
PROD_DEPLOY_USER=deploy
PROD_DEPLOY_SSH_KEY=-----BEGIN RSA PRIVATE KEY-----...
PROD_DEPLOY_PORT=22
PROD_DB_HOST=prod-mysql-host
PROD_DB_PORT=3306
PROD_DB_DATABASE=merchbay_admin
PROD_DB_USERNAME=prod_user
PROD_DB_PASSWORD=prod_password
Shared Secrets (if using same server)
DEPLOY_HOST=your-server-ip
DEPLOY_USER=deploy
DEPLOY_SSH_KEY=-----BEGIN RSA PRIVATE KEY-----...
DEPLOY_PORT=22
Verification
Check Development SSL
# Check certificate issuer (should be Let's Encrypt)
echo | openssl s_client -servername dev.merchbay.app -connect dev.merchbay.app:443 2>/dev/null | openssl x509 -noout -issuer
# Should show: issuer=C = US, O = Let's Encrypt, CN = R3
Check Production SSL
# Check certificate issuer (should be your SSL provider)
echo | openssl s_client -servername merchbay.app -connect merchbay.app:443 2>/dev/null | openssl x509 -noout -issuer
# Should show your paid SSL provider
# Check certificate validity
echo | openssl s_client -servername merchbay.app -connect merchbay.app:443 2>/dev/null | openssl x509 -noout -dates
Verify in Browser
-
Visit https://dev.merchbay.app
- Certificate should be issued by "Let's Encrypt Authority X3"
-
Visit https://merchbay.com
- Certificate should be issued by your paid SSL provider
Troubleshooting
Development SSL Not Working
# Check Let's Encrypt logs
docker logs traefik | grep letsencrypt
# Verify acme.json permissions
ls -l /path/to/letsencrypt/acme.json
# Should be: -rw------- (600)
# Check DNS
dig dev.merchbay.app +short
# Should return your server IP
Production SSL Not Working
# Verify Traefik can read certificates
docker exec traefik ls -l /srv/certs/
# Check dynamic configuration is loaded
docker exec traefik cat /etc/traefik/dynamic/certs.yml
# Verify certificate format
openssl x509 -in /srv/certs/merchbay.app-fullchain.crt -text -noout
# Check private key
openssl rsa -in /srv/certs/merchbay.app.key -check
Certificate Mismatch
# Verify certificate and key match
openssl x509 -noout -modulus -in /srv/certs/merchbay.app.crt | openssl md5
openssl rsa -noout -modulus -in /srv/certs/merchbay.app.key | openssl md5
# Both should output the same hash
Renewing Certificates
Development (Let's Encrypt)
Automatic renewal every 60 days. No action needed.
Production (Paid SSL)
Before certificate expiration:
- Download new certificate from your SSL provider
- Update files in
/srv/certs/ - Restart Traefik:
docker compose restart traefik - Verify:
curl -vI https://merchbay.app
DNS Configuration
Development
Type: A
Name: dev.merchbay.app
Value: YOUR_SERVER_IP
TTL: 3600
Production
Type: A
Name: merchbay.app (or @)
Value: YOUR_SERVER_IP
TTL: 3600
Type: A
Name: www.merchbay.app
Value: YOUR_SERVER_IP
TTL: 3600
Security Best Practices
- Keep certificates private: Never commit SSL keys to git
- Use strong permissions: 600 for private keys, 644 for certificates
- Monitor expiration: Set reminders 30 days before expiration
- Use HSTS: Add header after SSL is working correctly
- Enable OCSP stapling: Improves SSL performance
- Regular updates: Keep Traefik updated for security patches