# Environment Variables Migration Guide This guide helps you migrate from the old fragile environment variable setup to the new clean, robust system. ## What Changed? ### Before (Fragile & Overkill) ❌ **Problems:** - Environment variables passed individually via SSH (12+ vars) - Duplicate definitions in Dockerfile, docker-compose.yml, and deploy.yml - Build args included runtime-only variables (SENTRY_DSN, MAIL_*, REDIS_*) - No single source of truth - Difficult to maintain and error-prone ```yaml # Old deploy.yml - FRAGILE! ssh root@alpha.mintel.me \ "MAIL_FROM='${{ secrets.MAIL_FROM }}' \ MAIL_HOST='${{ secrets.MAIL_HOST }}' \ MAIL_PASSWORD='${{ secrets.MAIL_PASSWORD }}' \ MAIL_PORT='${{ secrets.MAIL_PORT }}' \ MAIL_RECIPIENTS='${{ secrets.MAIL_RECIPIENTS }}' \ MAIL_USERNAME='${{ secrets.MAIL_USERNAME }}' \ NEXT_PUBLIC_BASE_URL='${{ secrets.NEXT_PUBLIC_BASE_URL }}' \ ... (12+ variables) \ /home/deploy/deploy.sh" ``` ### After (Clean & Robust) ✅ **Benefits:** - Single `.env` file on server contains all runtime variables - Only `NEXT_PUBLIC_*` variables passed as build args (3 vars) - Clear separation: build-time vs runtime - Easy to maintain and update - Single source of truth per environment ```yaml # New deploy.yml - CLEAN! ssh root@alpha.mintel.me "/home/deploy/deploy.sh" ``` ## Migration Steps ### Step 1: Update Gitea Secrets **Remove these secrets** (no longer needed in CI/CD): - ❌ `MAIL_FROM` - ❌ `MAIL_HOST` - ❌ `MAIL_PASSWORD` - ❌ `MAIL_PORT` - ❌ `MAIL_RECIPIENTS` - ❌ `MAIL_USERNAME` - ❌ `NODE_ENV` - ❌ `REDIS_URL` - ❌ `REDIS_KEY_PREFIX` - ❌ `SENTRY_DSN` (from build args) **Keep these secrets** (still needed for build): - ✅ `NEXT_PUBLIC_BASE_URL` - ✅ `NEXT_PUBLIC_UMAMI_WEBSITE_ID` - ✅ `NEXT_PUBLIC_UMAMI_SCRIPT_URL` - ✅ `REGISTRY_USER` - ✅ `REGISTRY_PASS` - ✅ `ALPHA_SSH_KEY` - ✅ `GOTIFY_URL` - ✅ `GOTIFY_TOKEN` ### Step 2: Create .env File on Server SSH to the production server and create the `.env` file: ```bash ssh root@alpha.mintel.me # Create .env file cat > /home/deploy/sites/klz-cables.com/.env << 'EOF' # Application NODE_ENV=production NEXT_PUBLIC_BASE_URL=https://klz-cables.com # Analytics NEXT_PUBLIC_UMAMI_WEBSITE_ID=your-actual-id NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js # Error Tracking SENTRY_DSN=your-actual-dsn # Email MAIL_HOST=smtp.eu.mailgun.org MAIL_PORT=587 MAIL_USERNAME=your-actual-username MAIL_PASSWORD=your-actual-password MAIL_FROM=KLZ Cables MAIL_RECIPIENTS=info@klz-cables.com # Redis REDIS_URL=redis://redis:6379/2 REDIS_KEY_PREFIX=klz: # Varnish VARNISH_CACHE_SIZE=256m EOF # Secure the file chmod 600 /home/deploy/sites/klz-cables.com/.env chown deploy:deploy /home/deploy/sites/klz-cables.com/.env ``` **Important**: Replace all `your-actual-*` placeholders with real values from your old Gitea secrets. ### Step 3: Update Deployment Script Update `/home/deploy/deploy.sh` to use the new approach: ```bash cat > /home/deploy/deploy.sh << 'EOF' #!/bin/bash set -e PROJECT_DIR="/home/deploy/sites/klz-cables.com" echo "╔══════════════════════════════════════════════════════════════════════════════╗" echo "║ KLZ Cables - Deployment Script ║" echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" cd "$PROJECT_DIR" # Check if .env file exists if [ ! -f .env ]; then echo "❌ ERROR: .env file not found at $PROJECT_DIR/.env" exit 1 fi echo "🔐 Logging into Docker registry..." echo "$REGISTRY_PASS" | docker login registry.infra.mintel.me -u "$REGISTRY_USER" --password-stdin echo "🔄 Pulling latest image..." docker pull registry.infra.mintel.me/mintel/klz-cables.com:latest echo "🔄 Stopping existing containers..." docker-compose down echo "🚀 Starting new containers..." docker-compose up -d echo "⏳ Waiting for services to be healthy..." sleep 10 echo "🔍 Checking service status..." docker-compose ps echo "" echo "✅ Deployment complete!" EOF chmod +x /home/deploy/deploy.sh ``` ### Step 4: Deploy Updated Code The new code is already in the repository. Just push to trigger deployment: ```bash git push origin main ``` The CI/CD workflow will: 1. Build with only `NEXT_PUBLIC_*` build args 2. Push to registry 3. SSH to server and run deploy.sh 4. Deploy.sh will use the `.env` file for runtime vars ### Step 5: Verify Migration After deployment, verify everything works: ```bash # SSH to server ssh root@alpha.mintel.me # Check containers are running cd /home/deploy/sites/klz-cables.com docker-compose ps # Verify environment variables are loaded docker-compose exec app env | grep -E "NODE_ENV|NEXT_PUBLIC|MAIL|REDIS" # Check application logs docker-compose logs -f app # Test the website curl -I https://klz-cables.com ``` ## Comparison Table | Aspect | Before | After | |--------|--------|-------| | **Gitea Secrets** | 15+ secrets | 8 secrets | | **Build Args** | 4 vars (including runtime-only) | 3 vars (NEXT_PUBLIC_* only) | | **Runtime Vars** | Passed via SSH command | Loaded from .env file | | **Maintenance** | Update in 3 places | Update in 1 place | | **Security** | Secrets in CI logs | Secrets only on server | | **Clarity** | Confusing duplication | Clear separation | | **Robustness** | Fragile SSH command | Robust file-based config | ## Rollback Plan If you need to rollback to the old system: 1. Revert the changes in git: ```bash git revert HEAD git push origin main ``` 2. Re-add the removed Gitea secrets 3. The old deployment will work again ## FAQ **Q: Why keep `NEXT_PUBLIC_*` in both build args and .env file?** A: `NEXT_PUBLIC_*` variables are special in Next.js - they're embedded into the JavaScript bundle at build time. They must be provided as build args. However, they're also needed at runtime for server-side rendering, so they're in the .env file too. **Q: Can I update environment variables without rebuilding?** A: Yes, for runtime-only variables (MAIL_*, REDIS_*, SENTRY_DSN, etc.). Just edit the `.env` file on the server and restart containers: ```bash nano /home/deploy/sites/klz-cables.com/.env docker-compose down && docker-compose up -d ``` For `NEXT_PUBLIC_*` variables, you need to rebuild the Docker image since they're baked into the client bundle. **Q: Where should I store the .env file backup?** A: Keep a secure backup outside the server: ```bash # Download from server scp root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/.env \ ~/secure-backups/klz-cables.env.backup # Store in password manager or encrypted storage ``` **Q: What if I accidentally commit .env to git?** A: 1. Remove it immediately: `git rm .env && git commit -m "Remove .env"` 2. Rotate all credentials in the file 3. Update the `.gitignore` to ensure it doesn't happen again (already done) ## Support If you encounter issues during migration: 1. Check the logs: `docker-compose logs -f app` 2. Verify .env file exists and has correct permissions 3. Ensure all required variables are set 4. Review [DEPLOYMENT.md](./DEPLOYMENT.md) for detailed troubleshooting ## Summary The new system is: - ✅ **Simpler**: One .env file instead of scattered variables - ✅ **Cleaner**: Clear separation of build vs runtime - ✅ **Robust**: File-based config instead of fragile SSH commands - ✅ **Secure**: Secrets stay on server, not in CI logs - ✅ **Maintainable**: Single source of truth per environment You've successfully migrated from a fragile, overkill setup to a clean, production-ready configuration! 🎉