# Traefik Deployment Guide This guide explains how to deploy the PicPeak application with Traefik as the reverse proxy. ## Overview The application consists of: - **Frontend**: React app served by nginx (port 80) - **Backend**: Node.js API (port 3000) - **Database**: PostgreSQL (port 5432, internal only) ## Traefik Configuration ### 1. Docker Labels for Traefik Add these labels to your `docker-compose.prod.yml` services: ```yaml services: frontend: labels: - "traefik.enable=true" - "traefik.http.routers.picpeak-frontend.rule=Host(`picpeak.yourdomain.com`)" - "traefik.http.routers.picpeak-frontend.entrypoints=websecure" - "traefik.http.routers.picpeak-frontend.tls.certresolver=letsencrypt" - "traefik.http.services.picpeak-frontend.loadbalancer.server.port=80" # Priority for catch-all route - "traefik.http.routers.picpeak-frontend.priority=1" backend: labels: - "traefik.enable=true" - "traefik.http.routers.picpeak-api.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/api`)" - "traefik.http.routers.picpeak-api.entrypoints=websecure" - "traefik.http.routers.picpeak-api.tls.certresolver=letsencrypt" - "traefik.http.services.picpeak-api.loadbalancer.server.port=3000" # Higher priority for API routes - "traefik.http.routers.picpeak-api.priority=10" # Additional routes for backend static files - "traefik.http.routers.picpeak-uploads.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/uploads`)" - "traefik.http.routers.picpeak-uploads.entrypoints=websecure" - "traefik.http.routers.picpeak-uploads.tls.certresolver=letsencrypt" - "traefik.http.routers.picpeak-uploads.service=picpeak-api" - "traefik.http.routers.picpeak-uploads.priority=10" - "traefik.http.routers.picpeak-images.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/images`)" - "traefik.http.routers.picpeak-images.entrypoints=websecure" - "traefik.http.routers.picpeak-images.tls.certresolver=letsencrypt" - "traefik.http.routers.picpeak-images.service=picpeak-api" - "traefik.http.routers.picpeak-images.priority=10" ``` ### 2. Network Configuration Ensure your services are on the Traefik network: ```yaml networks: picpeak: external: false traefik: external: true services: frontend: networks: - picpeak - traefik backend: networks: - picpeak - traefik db: networks: - picpeak # Don't expose to traefik ``` ### 3. Remove Nginx Service Since you're using Traefik, remove the nginx service from `docker-compose.prod.yml`: ```yaml # Remove this entire service: # nginx: # image: nginx:alpine # ... ``` ## Frontend Configuration The frontend is built with the API URL set to `/api`. This is important because: 1. All API calls will be relative to the same domain 2. Traefik will route `/api/*` to the backend service 3. No CORS issues since everything is on the same domain ## Environment Variables Ensure these are set correctly: ```bash # Backend needs to know the public URLs ADMIN_URL=https://picpeak.yourdomain.com FRONTEND_URL=https://picpeak.yourdomain.com # Backend API is accessed via /api path API_URL=https://picpeak.yourdomain.com/api ``` ## Complete Example Here's a complete `docker-compose.prod.yml` for Traefik: ```yaml version: '3.8' networks: picpeak: external: false traefik: external: true services: backend: image: picpeak-backend:latest build: context: ./backend dockerfile: Dockerfile restart: unless-stopped depends_on: - db environment: - NODE_ENV=production - PORT=3000 - JWT_SECRET=${JWT_SECRET} - ADMIN_URL=https://picpeak.yourdomain.com - FRONTEND_URL=https://picpeak.yourdomain.com - DATABASE_CLIENT=pg - DB_HOST=db - DB_PORT=5432 - DB_USER=${DB_USER:-picpeak} - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=${DB_NAME:-picpeak} - SMTP_HOST=${SMTP_HOST} - SMTP_PORT=${SMTP_PORT} - SMTP_SECURE=${SMTP_SECURE} - SMTP_USER=${SMTP_USER} - SMTP_PASS=${SMTP_PASS} - EMAIL_FROM=${EMAIL_FROM} - STORAGE_PATH=/app/storage - EVENTS_PATH=/app/storage/events - ARCHIVE_PATH=/app/storage/events/archived volumes: - ./storage:/app/storage - ./data:/app/data - ./logs:/app/logs networks: - picpeak - traefik labels: - "traefik.enable=true" - "traefik.http.routers.picpeak-api.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/api`)" - "traefik.http.routers.picpeak-api.entrypoints=websecure" - "traefik.http.routers.picpeak-api.tls.certresolver=letsencrypt" - "traefik.http.services.picpeak-api.loadbalancer.server.port=3000" - "traefik.http.routers.picpeak-api.priority=10" - "traefik.http.routers.picpeak-uploads.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/uploads`)" - "traefik.http.routers.picpeak-uploads.entrypoints=websecure" - "traefik.http.routers.picpeak-uploads.tls.certresolver=letsencrypt" - "traefik.http.routers.picpeak-uploads.service=picpeak-api" - "traefik.http.routers.picpeak-uploads.priority=10" - "traefik.http.routers.picpeak-images.rule=Host(`picpeak.yourdomain.com`) && PathPrefix(`/images`)" - "traefik.http.routers.picpeak-images.entrypoints=websecure" - "traefik.http.routers.picpeak-images.tls.certresolver=letsencrypt" - "traefik.http.routers.picpeak-images.service=picpeak-api" - "traefik.http.routers.picpeak-images.priority=10" frontend: image: picpeak-frontend:latest build: context: ./frontend dockerfile: Dockerfile args: - VITE_API_URL=/api restart: unless-stopped depends_on: - backend networks: - picpeak - traefik labels: - "traefik.enable=true" - "traefik.http.routers.picpeak-frontend.rule=Host(`picpeak.yourdomain.com`)" - "traefik.http.routers.picpeak-frontend.entrypoints=websecure" - "traefik.http.routers.picpeak-frontend.tls.certresolver=letsencrypt" - "traefik.http.services.picpeak-frontend.loadbalancer.server.port=80" - "traefik.http.routers.picpeak-frontend.priority=1" db: image: postgres:14-alpine restart: unless-stopped environment: - POSTGRES_USER=${DB_USER:-picpeak} - POSTGRES_PASSWORD=${DB_PASSWORD} - POSTGRES_DB=${DB_NAME:-picpeak} - POSTGRES_HOST_AUTH_METHOD=scram-sha-256 - POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 --auth-local=trust volumes: - postgres_data:/var/lib/postgresql/data networks: - picpeak command: postgres -c ssl=off volumes: postgres_data: ``` ## Troubleshooting ### 502 Bad Gateway Errors 1. **Check if backend is running**: ```bash docker-compose -f docker-compose.prod.yml ps docker-compose -f docker-compose.prod.yml logs backend ``` 2. **Verify Traefik can reach the backend**: - Ensure both services are on the same Docker network - Check Traefik logs: `docker logs traefik` 3. **Check backend health**: ```bash docker-compose -f docker-compose.prod.yml exec backend curl http://localhost:3000/api/health ``` ### Frontend Can't Reach API 1. **Verify API paths don't have double `/api`**: - Frontend should call `/auth/admin/login`, not `/api/auth/admin/login` - The base URL in axios should be `/api` 2. **Check browser console for actual URLs being called** 3. **Ensure Traefik routing rules are correct**: - API routes should have higher priority than frontend catch-all ### CORS Issues Should not occur since everything is on the same domain. If you see CORS errors: 1. Check that `FRONTEND_URL` and `ADMIN_URL` match your actual domain 2. Ensure you're not mixing HTTP and HTTPS ## Testing the Setup 1. **Test API directly**: ```bash curl https://picpeak.yourdomain.com/api/health ``` 2. **Test frontend**: ```bash curl https://picpeak.yourdomain.com/ ``` 3. **Test admin login**: - Navigate to https://picpeak.yourdomain.com/admin/login - Check browser console for any errors ## Important Notes 1. **SSL/TLS**: Traefik handles SSL termination, so the backend doesn't need SSL 2. **Port Exposure**: Don't expose backend ports directly - let Traefik handle routing 3. **Health Checks**: Configure Traefik health checks for better reliability 4. **Rate Limiting**: Consider adding Traefik rate limiting middleware for API routes