Production Server Configuration Guide

Multi-Application Deployment with Traefik

This document provides a comprehensive overview of the production server configuration for deploying containerized applications alongside the existing ETF web application.


πŸ“‹ Server Overview

Server Details

  • IP Address: 31.97.61.154
  • SSH Access: root@31.97.61.154
  • SSH Key: key.file (ED25519, passphrase-protected)
  • Operating System: Ubuntu (Linux)
  • Location: Remote VPS/Cloud Server

SSH Connection

# Add SSH key to agent (required once per session)
ssh-add /path/to/key.file

# Connect to server
ssh root@31.97.61.154

πŸ—οΈ Infrastructure Architecture

Traefik Reverse Proxy (Core Component)

The server uses Traefik as the central reverse proxy for all containerized applications. This provides:

  • Automatic SSL/TLS certificate management (Let’s Encrypt)
  • HTTP to HTTPS redirection
  • Dynamic service discovery via Docker labels
  • Centralized security headers and middleware
  • Multi-application routing on a single domain/IP

Network Architecture

Internet (Port 80/443)
          ↓
    Traefik Proxy
          ↓
    β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    ↓                       ↓              ↓
ETF Web App          Your New App     Other Apps
(etf.righttime.com.au)  (subdomain)   (subdomains)

🌐 Current ETF Application Configuration

Application Directory Structure

~/etf-app/
β”œβ”€β”€ docker-compose.etf.yml    # Main compose file
β”œβ”€β”€ .env.prod                 # Production environment variables
β”œβ”€β”€ backend/                  # FastAPI backend
β”œβ”€β”€ frontend/                 # Next.js frontend
β”œβ”€β”€ jobs/                     # Scheduler service
β”œβ”€β”€ docker/                   # Custom Docker configs
└── S6.dump                   # Database backup/restore file

Services Running

| Service | Container Name | Internal Port | Domain/Path | |β€”β€”β€”|β€”β€”β€”β€”β€”|β€”β€”β€”β€”β€”|β€”β€”β€”β€”-| | Frontend | etf-app_frontend_1 | 3000 | etf.righttime.com.au/ | | Backend API | etf-app_backend_1 | 8000 | etf.righttime.com.au/api | | PostgreSQL | ae3691d0b559_etf-app_postgres_1 | 5432 | Internal only | | Redis | 90250ffc70ca_etf-app_redis_1 | 6379 | Internal only | | Scheduler | 375fda2c5db7_etf-app_scheduler_1 | N/A | Background job | | Jaeger | (internal) | 16686, 6831 | Internal only |

Docker Networks

  • etf_net - Internal bridge network for ETF app services
  • traefik-public - External network shared with Traefik (CRITICAL for routing)

πŸ” Traefik Configuration

Traefik Middleware File

Location: ~/traefik/middlewares.yml

This file contains reusable middleware configurations that apply security headers, rate limiting, compression, and other features to all applications.

Key Middleware Components

1. Security Headers
# For API/Backend services (restrictive CSP)
backend-security:
  - Content Security Policy: No unsafe directives
  - HSTS: Enabled (31536000 seconds)
  - X-Frame-Options: DENY
  - X-Content-Type-Options: nosniff
  - Rate limiting and compression included

# For Frontend services (balanced CSP)
frontend-security:
  - Content Security Policy: Allows inline scripts/styles (for React/Next.js)
  - HSTS: Enabled (31536000 seconds)
  - X-Frame-Options: DENY
  - Supports CDNs: cdnjs, jsdelivr, plot.ly
  - connect-src: Allows *.righttime.com.au
2. HTTPS Redirects
# Automatic HTTP β†’ HTTPS redirect
backend-https-redirect
frontend-https-redirect
3. Rate Limiting
api-rate-limit:      # 100 req/s, burst 200
auth-rate-limit:     # 5 req/s, burst 10
4. Compression
compression:         # Gzip for responses >1KB
5. Authentication & IP Whitelisting
auth:                # Basic auth for dashboards
ipwhitelist-dashboard: # IP: 27.32.38.220/32

How Traefik Discovers Services

Traefik automatically discovers Docker containers using Docker labels. Services must:

  1. Be on the traefik-public network
  2. Have traefik.enable=true label
  3. Define routing rules via labels

πŸš€ Deploying a New Application

Step 1: Prepare Your Application

Required Docker Compose Configuration

Your new application’s docker-compose.yml must include:

version: '3.8'

services:
  your-app:
    image: your-image:latest
    expose:
      - "8080"  # Internal port only (no 'ports:' mapping)
    networks:
      - your-internal-network
      - traefik-public  # REQUIRED: Connect to Traefik
    labels:
      # Enable Traefik discovery
      - "traefik.enable=true"
      
      # HTTPS Router (main)
      - "traefik.http.routers.yourapp.rule=Host(`yourapp.righttime.com.au`)"
      - "traefik.http.routers.yourapp.entrypoints=websecure"
      - "traefik.http.routers.yourapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.yourapp.loadbalancer.server.port=8080"
      - "traefik.docker.network=traefik-public"
      
      # HTTP Router (redirect to HTTPS)
      - "traefik.http.routers.yourapp-http.rule=Host(`yourapp.righttime.com.au`)"
      - "traefik.http.routers.yourapp-http.entrypoints=web"
      - "traefik.http.routers.yourapp-http.middlewares=redirect-to-https@file"
      
      # Apply security middleware
      - "traefik.http.routers.yourapp.middlewares=backend-security@file,compression@file,api-rate-limit@file"

networks:
  your-internal-network:
    driver: bridge
  traefik-public:
    external: true  # REQUIRED: Must be external

Subdomain Routing Patterns

Option A: Subdomain Routing (Recommended)

- "traefik.http.routers.yourapp.rule=Host(`app.righttime.com.au`)"

Option B: Path-Based Routing

- "traefik.http.routers.yourapp.rule=Host(`etf.righttime.com.au`) && PathPrefix(`/yourapp`)"

Option C: Separate Domain

- "traefik.http.routers.yourapp.rule=Host(`yourdomain.com`)"

Step 2: DNS Configuration

For Subdomain Routing:

  • Add A record: yourapp.righttime.com.au β†’ 31.97.61.154
  • Wait for DNS propagation (5-60 minutes)

For Path-Based Routing:

  • No DNS changes needed (uses existing domain)

Step 3: Deploy to Server

# 1. Create application directory
ssh root@31.97.61.154 "mkdir -p ~/your-app"

# 2. Copy files to server
scp -r ./* root@31.97.61.154:~/your-app/

# 3. Deploy containers
ssh root@31.97.61.154 "cd ~/your-app && docker-compose up -d"

# 4. Verify Traefik sees your service
ssh root@31.97.61.154 "docker logs traefik-container-name 2>&1 | grep 'yourapp'"

# 5. Test HTTPS endpoint
curl -I https://yourapp.righttime.com.au

Step 4: Verify Deployment

# Check container is running
docker ps | grep your-app

# Check container is on traefik-public network
docker network inspect traefik-public

# Test HTTPS access
curl https://yourapp.righttime.com.au

# Verify SSL certificate
openssl s_client -connect yourapp.righttime.com.au:443 -servername yourapp.righttime.com.au

πŸ”§ Traefik Middleware Usage Examples

Example 1: Public API Service

labels:
  - "traefik.http.routers.api.middlewares=backend-security@file,compression@file,api-rate-limit@file"

Example 2: Public Web Application

labels:
  - "traefik.http.routers.web.middlewares=frontend-security@file,compression@file"

Example 3: Protected Admin Dashboard

labels:
  - "traefik.http.routers.admin.middlewares=auth@file,ipwhitelist-dashboard@file,security-headers@file"

Example 4: Authenticated API

labels:
  - "traefik.http.routers.secure-api.middlewares=auth-rate-limit@file,backend-security@file"

πŸ“ Available Middleware Reference

Copy from ~/traefik/middlewares.yml on the production server:

Middleware Purpose Use Case
backend-security@file Strict API security headers REST APIs, GraphQL endpoints
frontend-security@file Balanced web app security React, Next.js, Vue apps
security-headers@file General security headers Static sites, basic apps
redirect-to-https@file HTTP β†’ HTTPS redirect All public services
backend-https-redirect@file API-specific redirect Backend services
frontend-https-redirect@file Frontend-specific redirect Frontend services
compression@file Gzip compression All services
api-rate-limit@file 100 req/s limit Public APIs
auth-rate-limit@file 5 req/s limit Authentication endpoints
auth@file Basic authentication Protected dashboards
ipwhitelist-dashboard@file IP restriction (27.32.38.220) Admin access
admin-whitelist@file Extended IP whitelist Internal tools

Important: All middleware references MUST use the @file suffix when defined in middlewares.yml.


πŸ›‘οΈ Security Best Practices

1. Network Isolation

  • Keep application services on internal networks (expose: not ports:)
  • Only connect to traefik-public for services that need external access
  • Use separate networks for each application stack

2. Environment Variables

  • Store all secrets in .env files (never commit to git)
  • Use strong, unique passwords for databases
  • Rotate credentials regularly

3. SSL/TLS

  • Let’s Encrypt handles automatic certificate renewal via Traefik
  • Certificates are stored in Traefik’s acme.json file
  • Force HTTPS for all services

4. Rate Limiting

  • Apply rate limiting to public APIs
  • Use stricter limits for authentication endpoints
  • Monitor for abuse via Traefik logs

5. Database Access

  • Never expose database ports externally
  • Use internal Docker networking only
  • Regular backups (like the ETF app’s S6.dump approach)

πŸ“Š Resource Management

Current Resource Allocation (ETF App)

Service CPU Limit Memory Limit CPU Reserved Memory Reserved
PostgreSQL 1 core 2GB 0.5 core 1GB
Redis 0.5 core 1GB 0.25 core 512MB
Backend 1 core 2GB 0.5 core 1GB
Frontend 0.5 core 1GB 0.25 core 512MB
Scheduler 0.5 core 1GB 0.25 core 512MB
Jaeger 0.25 core 512MB 0.1 core 256MB
deploy:
  resources:
    limits:
      cpus: '0.5'
      memory: 1G
    reservations:
      cpus: '0.25'
      memory: 512M

Note: Monitor server resources with docker stats to avoid overcommitment.


πŸ”„ Deployment Automation

Creating a Deployment Script (Like deploy-prod.sh)

#!/bin/bash
set -e

REMOTE_HOST="root@31.97.61.154"
REMOTE_DIR="~/your-app"
COMPOSE_FILE="docker-compose.yml"
ENV_FILE=".env.prod"

# Deploy files
scp ${COMPOSE_FILE} ${REMOTE_HOST}:${REMOTE_DIR}/
scp ${ENV_FILE} ${REMOTE_HOST}:${REMOTE_DIR}/

# Build and deploy
ssh ${REMOTE_HOST} "cd ${REMOTE_DIR} && docker-compose up -d --build"

# Health check
sleep 10
ssh ${REMOTE_HOST} "docker ps | grep your-app"

echo "βœ… Deployment complete!"
echo "🌐 Access: https://yourapp.righttime.com.au"

πŸ” Monitoring & Maintenance

Health Checks

# Check all containers
ssh root@31.97.61.154 "docker ps --format 'table \t'"

# Check specific app
ssh root@31.97.61.154 "docker-compose -f ~/your-app/docker-compose.yml ps"

# View logs
ssh root@31.97.61.154 "docker logs your-container-name -f"

Traefik Dashboard Access

# Check if Traefik dashboard is enabled
ssh root@31.97.61.154 "docker inspect traefik-container | grep -A 10 'api'"

# Access via browser (if enabled):
# https://traefik.righttime.com.au/dashboard/

Resource Monitoring

# Real-time resource usage
ssh root@31.97.61.154 "docker stats"

# Disk usage
ssh root@31.97.61.154 "df -h"

# Docker volume usage
ssh root@31.97.61.154 "docker system df"

🚨 Troubleshooting

Common Issues

1. Service Not Accessible (404/503)

# Check container is running
docker ps | grep your-app

# Check Traefik sees the service
docker logs traefik-container 2>&1 | grep yourapp

# Verify network connection
docker network inspect traefik-public | grep your-container

# Check labels are correct
docker inspect your-container | grep -A 20 Labels

2. SSL Certificate Not Generated

# Check Traefik logs for Let's Encrypt errors
docker logs traefik-container | grep -i "acme\|certificate"

# Verify DNS points to server
dig yourapp.righttime.com.au

# Check port 80/443 are accessible
telnet 31.97.61.154 443

3. Middleware Not Applied

# Verify middleware file exists
ssh root@31.97.61.154 "cat ~/traefik/middlewares.yml"

# Check for @file suffix in labels
docker inspect your-container | grep middleware

# Restart Traefik to reload config
docker restart traefik-container

4. Container Can’t Connect to Database

# Verify both containers are on same network
docker network inspect your-internal-network

# Check database is healthy
docker ps | grep postgres

# Test connection from container
docker exec your-container ping postgres-container

πŸ“š ETF Application Specifics

Environment Variables (.env.prod)

The ETF application uses these key variables:

  • TRAEFIK_DOMAIN=etf.righttime.com.au
  • POSTGRES_USER=etf_user
  • POSTGRES_DB=etf_production
  • REDIS_PASSWORD=[secure-password]
  • DATABASE_URL=postgresql+asyncpg://etf_user:password@postgres:5432/etf_production

Database Management

# Backup database
ssh root@31.97.61.154 "cd ~/etf-app && docker-compose -f docker-compose.etf.yml --env-file .env.prod exec -T postgres pg_dump -U etf_user -Fc etf_production > backup.dump"

# Restore database
ssh root@31.97.61.154 "cd ~/etf-app && docker-compose -f docker-compose.etf.yml --env-file .env.prod exec -T postgres pg_restore -U etf_user -d etf_production --clean --if-exists --no-owner --no-acl < S6.dump"

# Clear Redis cache
ssh root@31.97.61.154 "cd ~/etf-app && docker-compose -f docker-compose.etf.yml --env-file .env.prod restart redis backend"

Refreshing Data

After database restore:

# Restart services to clear cache
docker-compose -f docker-compose.etf.yml --env-file .env.prod restart redis backend

# Refresh materialized views
docker-compose -f docker-compose.etf.yml --env-file .env.prod exec -T postgres \
  psql -U etf_user -d etf_production -c 'REFRESH MATERIALIZED VIEW market_high_conviction_signals;'

🎯 Quick Start Checklist for New App

  • Create docker-compose.yml with Traefik labels
  • Add service to traefik-public network (external)
  • Use expose: instead of ports: for internal services
  • Set routing rule: Host() or PathPrefix()
  • Apply appropriate middleware (@file suffix)
  • Configure DNS (if using subdomain)
  • Create deployment directory on server
  • Upload files via SCP
  • Run docker-compose up -d
  • Verify container health with docker ps
  • Test HTTPS access
  • Monitor logs for errors

πŸ“ž Support & Resources

Server Access

  • SSH: ssh root@31.97.61.154 (requires key.file)
  • Key Location: /Users/stevensmart/git/Foundry/S6web/key.file

Documentation Locations

  • ETF Deployment: /Users/stevensmart/git/Foundry/S6web/PRODUCTION-DEPLOYMENT.md
  • General Deployment: /Users/stevensmart/git/Foundry/S6web/DEPLOYMENT.md
  • This Guide: /Users/stevensmart/git/Foundry/S6web/PRODUCTION-SERVER-CONFIG.md

Key Files

  • Traefik Middleware Template: /Users/stevensmart/git/Foundry/S6web/traefik-middlewares.yml
  • ETF Docker Compose: /Users/stevensmart/git/Foundry/S6web/docker-compose.etf.yml
  • Deployment Script: /Users/stevensmart/git/Foundry/S6web/deploy-prod.sh

πŸ”„ Version History

Date Change Author
2025-10-11 Initial creation for multi-app deployment guidance System

Last Updated: 2025-10-11
Server IP: 31.97.61.154
Primary Domain: etf.righttime.com.au
Traefik Network: traefik-public