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 servicestraefik-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:
- Be on the
traefik-publicnetwork - Have
traefik.enable=truelabel - 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:notports:) - Only connect to
traefik-publicfor services that need external access - Use separate networks for each application stack
2. Environment Variables
- Store all secrets in
.envfiles (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 |
Recommended Limits for New Apps
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.auPOSTGRES_USER=etf_userPOSTGRES_DB=etf_productionREDIS_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-publicnetwork (external) - Use
expose:instead ofports:for internal services - Set routing rule:
Host()orPathPrefix() - Apply appropriate middleware (
@filesuffix) - 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