Full External Services Setup
December 1, 2025 · View on GitHub
This is a comprehensive production setup with ALL services externalized into separate containers. This configuration demonstrates how to run M3U Editor with all its internal services disabled, using dedicated containers for each component.
Architecture Overview
┌─────────────────────────────────────────────────────────┐
│ Nginx/Caddy │
│ (Reverse Proxy) │
│ Port: 80/443 │
└──────────────┬────────────────────┬─────────────────────┘
│ │
┌────────▼──────────┐ ┌─────▼──────────┐
│ M3U Editor │ │ M3U Proxy │
│ (PHP-FPM) │ │ (Streaming) │
│ Port: 9000 │ │ Port: 38085 │
└────────┬──────────┘ └─────┬──────────┘
│ │
┌────────▼────────────────────▼──────────┐
│ │
┌─────▼──────┐ ┌──────────────┐ │
│ PostgreSQL │ │ Redis │ │
│ Port: 5432 │ │ Port: 6379 │ │
└────────────┘ └──────────────┘ │
│
└─ Shared Network
Services Included
-
PostgreSQL (
postgres) - External database container- PostgreSQL 17 Alpine
- Persistent data volume
- Health checks configured
-
Redis (
redis) - External cache and stream pooling- Redis Alpine 3.22
- Configured for optimal caching
- Used by both m3u-editor and m3u-proxy
-
M3U Proxy (
m3u-proxy) - External streaming proxy- Handles all streaming requests
- Hardware acceleration support
- Redis-based connection pooling
-
M3U Editor (
m3u-editor) - Main application- ALL internal services DISABLED
- PHP-FPM only (no embedded nginx)
- Connects to external postgres, redis, and m3u-proxy
-
Nginx (
nginx) - External reverse proxy- Routes traffic to m3u-editor and m3u-proxy
- Handles SSL termination (when configured)
- Optimized for streaming
Disabled Internal Services
This configuration explicitly disables all internal services in m3u-editor:
- ✅
NGINX_ENABLED=false- Using external Nginx container - ✅
ENABLE_POSTGRES=false- Using external PostgreSQL container - ✅
REDIS_ENABLED=false- Using external Redis container - ✅
M3U_PROXY_ENABLED=false- Using external m3u-proxy container
Quick Start
1. Prerequisites
- Docker and Docker Compose installed
- At least 2GB RAM available
- Ports 8080 (or your chosen port) available
2. Setup Environment
# Copy the example environment file
cp .env.external-all.example .env
# Generate secure credentials
echo "PG_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "M3U_PROXY_TOKEN=$(openssl rand -hex 32)" >> .env
# Edit .env and update APP_URL with your port
# Example: APP_URL=http://localhost:8080
nano .env
3. Start Services
# Start all services
docker-compose -f docker-compose.external-all.yml up -d
# View logs (helpful to see startup progress)
docker-compose -f docker-compose.external-all.yml logs -f
# Check service health (wait for all services to show "healthy")
docker-compose -f docker-compose.external-all.yml ps
Note: Initial startup may take 90-120 seconds for:
- Database migrations to complete
- Services to become healthy
- PHP-FPM to initialize
4. Access Application
- Web Interface: http://localhost:8080 (or your configured
NGINX_PORT) - Default Access: Configure on first access
5. Troubleshooting
If services fail to start or you can't access the application:
# Run the automated troubleshooting script
./troubleshoot-external-services.sh
This script will check:
- Service health status
- Network connectivity between services
- Application endpoint accessibility
- Common configuration issues
6. Stop Services
# Stop all services
docker-compose -f docker-compose.external-all.yml down
# Stop and remove volumes (CAUTION: deletes all data)
docker-compose -f docker-compose.external-all.yml down -v
Configuration
Environment Variables
Key environment variables in .env:
# Application
APP_URL=http://localhost # Your domain or IP
APP_PORT=8080 # External HTTP port (used for routing and URL creation)
NGINX_PORT=8080 # External HTTP port
# Database
PG_DATABASE=m3ue
PG_USER=m3ue
PG_PASSWORD=<generate-secure-password>
# Redis
REDIS_PORT=6379
# M3U Proxy
M3U_PROXY_PORT=38085
M3U_PROXY_TOKEN=<generate-secure-token>
Nginx Configuration
The nginx.conf file includes:
- PHP-FPM routing - Proxies PHP requests to m3u-editor:9000
- M3U Proxy routing - Routes
/m3u-proxy/*to m3u-proxy:38085 - Static file serving - Optimized caching for assets
- Streaming optimization - Disabled buffering for live streams
- Security headers - Basic security hardening
To customize:
nano nginx.conf
# Restart nginx after changes
docker-compose -f docker-compose.external-all.yml restart nginx
SSL/HTTPS Configuration
To enable HTTPS:
-
Obtain SSL certificates (Let's Encrypt, commercial CA, etc.)
-
Uncomment HTTPS server block in
nginx.conf -
Mount certificates in
docker-compose.external-all.yml:
nginx:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro # Add this line
ports:
- "80:80"
- "443:443" # Uncomment this
- Update environment variables:
APP_URL=https://your-domain.com
Monitoring and Logs
View Logs
# All services
docker-compose -f docker-compose.external-all.yml logs -f
# Specific service
docker-compose -f docker-compose.external-all.yml logs -f m3u-editor
docker-compose -f docker-compose.external-all.yml logs -f nginx
docker-compose -f docker-compose.external-all.yml logs -f postgres
Health Checks
# Check all services
docker-compose -f docker-compose.external-all.yml ps
# All services should show (healthy) status
Database Access
# Connect to PostgreSQL
docker exec -it m3u-postgres psql -U m3ue -d m3ue
# Backup database
docker exec m3u-postgres pg_dump -U m3ue m3ue > backup.sql
# Restore database
docker exec -i m3u-postgres psql -U m3ue -d m3ue < backup.sql
Redis Access
# Connect to Redis CLI
docker exec -it m3u-redis redis-cli
# Monitor Redis commands
docker exec -it m3u-redis redis-cli MONITOR
# Check Redis memory usage
docker exec -it m3u-redis redis-cli INFO memory
Resource Limits
The compose file includes optional resource limits for each service:
- PostgreSQL: 2 CPU cores, 1GB RAM (limits)
- Redis: 1 CPU core, 512MB RAM (limits)
- M3U Proxy: 2 CPU cores, 2GB RAM (limits)
- M3U Editor: 2 CPU cores, 2GB RAM (limits)
- Nginx: 1 CPU core, 256MB RAM (limits)
These are configured but can be adjusted based on your needs.
Networking
All services communicate on the m3u-network bridge network:
- Services use container names as hostnames (e.g.,
postgres,redis,m3u-proxy) - Only Nginx exposes ports externally
- Internal service ports are not exposed for security
Volumes
Persistent data is stored in Docker volumes:
postgres-data- PostgreSQL databaseredis-data- Redis cacheapp-public- Shared volume between m3u-editor and nginx for static files (CSS, JS, images, fonts)./data- M3U Editor configuration (bind mount)
Note: The app-public volume is automatically populated by the m3u-editor container at startup. This contains the compiled frontend assets (from the build process) and is shared with nginx for efficient static file serving. This design allows the setup to work in production without requiring local source files.
Backup Volumes
# Backup PostgreSQL data
docker run --rm -v m3u-editor_postgres-data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz -C /data .
# Backup Redis data
docker run --rm -v m3u-editor_redis-data:/data -v $(pwd):/backup alpine tar czf /backup/redis-backup.tar.gz -C /data .
Troubleshooting
M3U Editor Container is Unhealthy
Symptoms:
dependency failed to start: container m3u-editor is unhealthy- Other services can't start due to m3u-editor dependency
Common Causes:
-
Database migrations still running: Wait 90-120 seconds for initial startup
# Watch the logs docker-compose -f docker-compose.external-all.yml logs -f m3u-editor -
PHP-FPM not responding: Check if PHP-FPM is listening on port 9000
# Test from nginx container docker exec m3u-nginx nc -zv m3u-editor 9000 -
Database connection failed: Verify postgres is healthy and credentials are correct
docker-compose -f docker-compose.external-all.yml ps postgres docker exec m3u-editor env | grep DB_ -
Missing required files: Ensure
publicdirectory existsls -la public/
Solution: Increase the health check start_period if startup is slow, or check logs for specific errors.
Services Won't Start
- Check if ports are available:
lsof -i :8080 # Check if your configured port is in use
- Check service logs:
docker-compose -f docker-compose.external-all.yml logs
- Verify environment variables:
docker-compose -f docker-compose.external-all.yml config
- Ensure required files exist:
# Check for required files
ls -la nginx.conf public/ .env
Database Connection Issues
- Verify PostgreSQL is healthy:
docker-compose -f docker-compose.external-all.yml ps postgres
- Test connection from m3u-editor:
docker exec m3u-editor nc -zv postgres 5432
- Check database credentials in
.env:
grep "^PG_\|^DB_" .env
- View PostgreSQL logs:
docker-compose -f docker-compose.external-all.yml logs postgres
Nginx 502 Bad Gateway
- Verify m3u-editor is healthy:
docker-compose -f docker-compose.external-all.yml ps m3u-editor
- Test PHP-FPM connectivity from nginx:
docker exec m3u-nginx nc -zv m3u-editor 9000
- Check m3u-editor logs:
docker-compose -f docker-compose.external-all.yml logs m3u-editor
- Verify shared volume is properly mounted:
docker exec m3u-nginx ls -la /var/www/html/public
docker exec m3u-editor ls -la /var/www/html/public
Static Files Not Loading (CSS/JS/Images)
Symptoms: Application loads but looks broken, no styling
Cause: The shared app-public volume is not properly populated or mounted
Solution:
- Verify the shared volume exists and is populated:
docker volume inspect m3u-editor_app-public
docker exec m3u-editor ls -la /var/www/html/public/build
- Check nginx volume mounts:
docker inspect m3u-nginx | grep -A 10 Mounts
- If the volume is empty, restart m3u-editor first, then nginx:
docker-compose -f docker-compose.external-all.yml restart m3u-editor
docker-compose -f docker-compose.external-all.yml restart nginx
Port Configuration Issues
Symptoms: Can't access application, wrong URLs generated
Cause: Mismatch between APP_URL, APP_PORT, and NGINX_PORT
Solution: Ensure consistency in .env:
# If using port 8080
APP_URL=http://localhost
APP_PORT=8080
NGINX_PORT=8080
After changing, restart services:
docker-compose -f docker-compose.external-all.yml restart m3u-editor nginx
Streaming Issues
- Verify m3u-proxy is healthy:
docker-compose -f docker-compose.external-all.yml ps m3u-proxy
- Check proxy logs:
docker-compose -f docker-compose.external-all.yml logs m3u-proxy
- Test m3u-proxy health endpoint:
# Get token from .env
M3U_TOKEN=$(grep M3U_PROXY_TOKEN .env | cut -d= -f2)
curl "http://localhost:8080/m3u-proxy/health?api_token=$M3U_TOKEN"
Performance Tuning
Database
Increase PostgreSQL memory in docker-compose.external-all.yml:
postgres:
command: postgres -c shared_buffers=256MB -c effective_cache_size=1GB
deploy:
resources:
limits:
memory: 2G # Increase from 1G
Redis
Increase Redis memory limit:
redis:
command: redis-server --maxmemory 512mb # Increase from 256mb
Nginx
Increase worker connections in nginx.conf:
events {
worker_connections 2048; # Increase from 1024
}
Security Considerations
- Change default credentials - Generate secure passwords for
PG_PASSWORDandM3U_PROXY_TOKEN - Use HTTPS - Configure SSL certificates for production
- Firewall rules - Only expose necessary ports (80/443)
- Keep updated - Regularly update Docker images
- Monitor access - Review nginx and application logs
- Backup regularly - Implement automated backup strategy
Comparison with Other Setups
| Feature | docker-compose.aio.yml | docker-compose.proxy.yml | docker-compose.proxy-vpn.yml | docker-compose.external-all.yml |
|---|---|---|---|---|
| Embedded Postgres | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No (external) |
| Embedded Nginx | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No (external) |
| Embedded Redis | ✅ Yes | ❌ No | ❌ No | ❌ No (external) |
| Embedded M3U Proxy | ✅ Yes | ❌ No | ❌ No | ❌ No (external) |
| External M3U Proxy | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
| External Redis | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
| External Postgres | ❌ No | ❌ No | ❌ No | ✅ Yes |
| External Nginx | ❌ No | ❌ No | ❌ No | ✅ Yes |
| VPN Support | ❌ No | ❌ No | ✅ Yes | ❌ No |
| Containers | 1 | 3 | 4 | 5 |
| Complexity | Very Low | Low | Medium | High |
| Flexibility | Very Low | Low | Medium | Very High |
| Best For | Development/Personal Use | Production | Production w/ VPN | Enterprise/Custom |