Email Reports - Dockerized Container
Unified Docker container for automated daily order email reports for both Crew Sportswear and MerchBay.
Overview
This container runs cron jobs daily at 23:55 CT to:
- Query orders from both Crew and MerchBay databases
- Generate separate CSV reports for each brand
- Email reports to recipients via SMTP
Cron Schedule:
- 23:55 - Crew Sportswear report
- 23:56 - MerchBay report
Quick Start
1. Create .env file
cp .env.example .env
vim .env
Configure your environment variables:
# Crew Database
DB_HOST_CREW=mysql
DB_NAME_CREW=custom_design
DB_USER_CREW=crew_user
DB_PASS_CREW=your_password
# MerchBay Database
DB_HOST_MERCHBAY=mysql
DB_NAME_MERCHBAY=merchbay_laravel
DB_USER_MERCHBAY=merchbay_user
DB_PASS_MERCHBAY=your_password
# SMTP
SMTP_PASS_CREW=crew_gmail_app_password
SMTP_PASS_MERCHBAY=merchbay_gmail_app_password
2. Build and Run
# Build image
docker-compose build
# Start container
docker-compose up -d
# Check logs
docker logs -f email_reports_crew
3. Test Manually
# Test Crew report
docker exec email_reports_unified BRAND=crew php /app/send_report.php
# Test MerchBay report
docker exec email_reports_unified BRAND=merchbay php /app/send_report.php
# Check cron log
docker exec email_reports_unified tail -f /var/log/cron.log
# Check email log (shows both brands)
docker exec email_reports_unified tail -f /app/email.log
┌──────────────────────────────────────────┐ │ Email Reports Container (Unified) │ │ │ │ ┌────────────────────────────────────┐ │ │ │ Cron Jobs: │ │ │ │ 23:55 → BRAND=crew │ │ │ │ 23:56 → BRAND=merchbay │ │ │ │ │ │ │ │ send_report.php │ │ │ │ └─ PHPMailer │ │ │ └────────────────────────────────────┘ │ │ │ │ └─────────────┼────────────────────────────┘ │ ├──> MySQL: custom_design (Crew) ├──> MySQL: merchbay_laravel (MerchBay) └──> SMTP: Gmail (2 accounts) │ ├──────> MySQL (crew-app-net) └──────> SMTP (smtp.gmail.com)
## Fsend_report.php** - Unified report generator (handles both brands)
- **index.php** - Legacy Crew report (kept for backward compatibility)
- **dbconfig.php** - Database connection (env-aware)
- **phpmailer/** - Email library
## Cron Schedule
```cron
55 23 * * * BRAND=crew php /app/send_report.php >> /var/log/cron.log 2>&1
56 23 * * * BRAND=merchbay php /app/send_report.php >> /var/log/cron.log 2>&1
23:55 CT - Crew Sportswear
23:56 CT - MerchBay
Both run daily, 1 minute apart. 55 23 * * * cd /app && php index.php >> /var/log/cron.log 2>&1
Runs daily at **11:55 PM Central Time**
## Environment Variables
### Crew Sportswear
| Variable | Default | Description |
|----------|---------|-------------|
| DB_HOST_CREW | mysql | MySQL host for Crew |
| DB_PORT_CREW | 3306 | MySQL port |
| DB_NAME_CREW | custom_design | Crew database name |
| DB_USER_CREW | crew_user | Crew database username |
| DB_PASS_CREW | - | Crew database password |
| SMTP_PASS_CREW | - | Gmail app password for mail@crewsportswear.com |
### MerchBay
| Variable | Default | Description |
|----------|---------|-------------|
| DB_HOST_MERCHBAY | mysql | MySQL host for MerchBay |
| DB_PORT_MERCHBAY | 3306 | MySQL port |
| DB_NAME_MERCHBAY | merchbay_laravel | MerchBay database name |
| DB_USER_MERCHBAY | merchbay_user | MerchBay database username |
| DB_PASS_MERCHBAY | - | MerchBay database password |
| SMTP_PASS_MERCHBAY | - | Gmail app password for support@merchbay.com |
### Shared
| Variable | Default | Description |
|----------|---------|-------------|
| SMTP_HOST | smtp.gmail.com | SMTP server |
| SMTP_PORT | 587 | SMTP port |
## Brand Configuration
The unified container handles both brands using the `BRAND` environment variable:
**Crew Sportswear (`BRAND=crew`)**:
- Database: `custom_design`
- From: `orders@crewsportswear.com`
- To: `graphics@crewsportswear.com`
- BCC: webmaster, angelo, production @crewsportswear.com
- Admin: https://admin.crewsportswear.app
- CSV: `daily_order_reports_crew/`
**MerchBay (`BRAND=merchbay`)**:
- Database: `merchbay_laravel`
- From: `orders@merchbay.com`
- To: `graphics@crewsportswear.com`
- BCC: webmaster, production @crewsportswear.com
- Admin: https://merchbay.app
- CSV: `daily_order_reports_merchbay/`
## Networks
Container must be on **crew-app-net** to access MySQL:
```yaml
networks:
- crew-app-net
networks:
crew-app-net:
external: true
Create network if it doesn't exist:
docker network create crew-app-net
Volumes
volumes:
- ./daily_order_reports_crew:/app/daily_order_reports_crew # Crew CSV archives
- ./daily_order_reports_merchbay:/app/daily_order_reports_merchbay # MerchBay CSV archives
- ./email.log:/app/email.log # Combined execution log
Both brands write to the same email.log with prefixes [CREW] and [MERCHBAY].
Deployment
Automated Deployment (Gitea Actions)
Production deployment is automated via Gitea Actions when pushing to main or master branch.
# Trigger deployment
git add .
git commit -m "Update email reports"
git push origin main
The workflow will:
- Build Docker image
- Transfer to production server via SSH
- Deploy to
/var/www/apps/email_reports - Start container with cron
- Verify container is running
Registry build is triggered by version tags:
# Create version tag
git tag v1.0.0
git push origin v1.0.0
This builds and pushes to your Docker registry.
Manual Deployment
# Deploy via docker-compose
docker-compose up -d --build
# Or build specific image version
docker build -t email-reports:1.0.0 .
docker tag email-reports:1.0.0 your-registry/email-reports:1.0.0
docker push your-registry/email-reports:1.0.0
Required Gitea Secrets
Configure in your repository settings:
DEPLOY_SSH_KEY- SSH private key for deployment serverDEPLOY_HOST- Deployment server hostname/IPDEPLOY_USER- SSH usernameREGISTRY_URL- Docker registry URL (for version tags)REGISTRY_USER- Registry usernameREGISTRY_PASSWORD- Registry password
Server Setup (First Time)
On your production server, create the deployment directory and .env file:
# Create directory
sudo mkdir -p /var/www/apps/email_reports
sudo chown $USER:$USER /var/www/apps/email_reports
cd /var/www/apps/email_reports
# Create .env file
cat > .env << 'EOL'
DB_HOST=mysql
DB_PORT=3306
DB_NAME=custom_design
DB_USER=crew_user
DB_PASS=your_secure_password
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=mail@crewsportswear.com
SMTP_PASS=your_gmail_app_password
EMAIL_TO=graphics@crewsportswear.com
EMAIL_BCC=webmaster@crewsportswear.com,angelo@crewsportswear.com,production@crewsportswear.com
APP_URL=https://www.crewsportswear.com
ADMIN_URL=https://admin.crewsportswear.app
EOL
chmod 600 .env
# Create data directories
mkdir -p daily_order_reports
touch email.log
chmod 666 email.log
# Ensure network exists
docker network create crew-app-net || true
Now push to trigger the first deployment!
Multiple Instances
For MerchBay, create similar setup in email_reports_merchbay/:
# docker-compose.yml in email_reports_merchbay/
services:
email-reports-merchbay:
build: .
container_name: email_reports_merchbay
environment:
- DB_NAME=merchbay_db
- EMAIL_TO=orders@merchbay.com
# ... other env vars
Monitoring
Check Container Health
# Container status
docker ps | grep email_reports
# Should show email_reports_unified
# Health check
docker inspect email_reports_unified | grep -A 5 Health
View Logs
# Real-time cron logs (both brands)
docker exec email_reports_unified tail -f /var/log/cron.log
# Email execution logs (prefixed by brand)
docker exec email_reports_unified tail -f /app/email.log
# Example log output:
# 2026-01-02 23:55:00 [CREW] successfully sent (5 orders)
# 2026-01-02 23:56:00 [MERCHBAY] No order for today
# Last 50 lines
docker logs --tail 50 email_reports_unified
# Filter by brand
docker exec email_reports_unified grep "\[CREW\]" /app/email.log | tail -10
docker exec email_reports_unified grep "\[MERCHBAY\]" /app/email.log | tail -10
Verify Cron Schedule
# View active crontab (should show both jobs)
docker exec email_reports_unified crontab -l
# Expected:
# 55 23 * * * BRAND=crew php /app/send_report.php >> /var/log/cron.log 2>&1
# 56 23 * * * BRAND=merchbay php /app/send_report.php >> /var/log/cron.log 2>&1
# Check if cron is running
docker exec email_reports_unified ps aux | grep crond
Troubleshooting
Database Connection Failed
# Test MySQL connectivity
docker exec email_reports_crew ping mysql
# Test database connection
docker exec email_reports_crew php -r "new PDO('mysql:host=mysql;dbname=custom_design', 'user', 'pass');"
# Check network
docker network inspect crew-app-net
Email Not Sending
# Enable debug mode
docker exec email_reports_crew sed -i 's|// \$mail->SMTPDebug|\$mail->SMTPDebug|' /app/index.php
# Run manually with debug
docker exec email_reports_crew php /app/index.php
# Check SMTP credentials in .env
Cron Not Running
# Check cron daemon
docker exec email_reports_crew ps aux | grep crond
# Restart container
docker restart email_reports_crew
# View crontab
docker exec email_reports_crew cat /etc/crontabs/root
Wrong Timezone
# Verify timezone
docker exec email_reports_crew date
docker exec email_reports_crew cat /etc/timezone
# Should show: America/Chicago
Permissions Issues
# Fix CSV directory permissions
docker exec email_reports_crew chmod 755 /app/daily_order_reports
# Fix log file
docker exec email_reports_crew chmod 666 /app/email.log
Maintenance
Update Cron Schedule
Edit Dockerfile and rebuild:
RUN echo "30 22 * * * cd /app && php index.php" > /etc/crontabs/root
Rotate Logs
# Manually clear logs (optional)
docker exec email_reports_crew truncate -s 0 /var/log/cron.log
docker exec email_reports_crew truncate -s 0 /app/email.log
Backup Reports
# Archive old CSV files
tar -czf daily_reports_backup_$(date +%Y%m%d).tar.gz daily_order_reports/
# Or sync to S3/backup location
Security
- Never commit
.env- Use.env.exampleonly - Use Gmail App Passwords - Not your main password
- Restrict network access - Only join necessary networks
- Rotate credentials - Change passwords regularly
- Monitor logs - Check for unauthorized access
Advantages of Separate Container
✅ Isolation - Reports run independently of web apps ✅ Reusability - Can serve multiple apps (Crew + MerchBay) ✅ Simple Deployment - Single purpose, easy to update ✅ Resource Control - Limit CPU/memory for cron jobs ✅ Independent Scaling - Restart without affecting web traffic ✅ Clean Separation - No coupling with Laravel/framework
Next Steps
- Test database connectivity
- Verify SMTP credentials
- Run manual test:
docker exec email_reports_crew php /app/index.php - Wait for 23:55 or adjust cron for testing
- Monitor logs for first automated run
- Set up similar container for MerchBay reports