Files
merchbay_admin/TRAEFIK-SSL-CONFIG.md
Frank John Begornia 63c5c50578
Some checks failed
Deploy Development / deploy (push) Failing after 1m18s
Update domain and application URL to dev-admin.merchbay.app across deployment configurations
2025-12-16 13:33:09 +08:00

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-admin.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 file
  • merchbay.app.key - Private key file
  • ca-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-admin.merchbay.app
  • SSL: Let's Encrypt (automatic)
  • Certificate Resolver: letsencrypt
labels:
  - "traefik.http.routers.merchbay-admin-dev.rule=Host(`dev-admin.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-admin.merchbay.app -connect dev-admin.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

  1. Visit https://dev-admin.merchbay.app

    • Certificate should be issued by "Let's Encrypt Authority X3"
  2. 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-admin.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:

  1. Download new certificate from your SSL provider
  2. Update files in /srv/certs/
  3. Restart Traefik: docker compose restart traefik
  4. Verify: curl -vI https://merchbay.app

DNS Configuration

Development

Type: A
Name: dev-admin.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

  1. Keep certificates private: Never commit SSL keys to git
  2. Use strong permissions: 600 for private keys, 644 for certificates
  3. Monitor expiration: Set reminders 30 days before expiration
  4. Use HSTS: Add header after SSL is working correctly
  5. Enable OCSP stapling: Improves SSL performance
  6. Regular updates: Keep Traefik updated for security patches