Files
klz-cables.com/docs/ENV_MIGRATION.md
Marc Mintel 21b16a5e6c
Some checks failed
Build & Deploy KLZ Cables / build-and-deploy (push) Failing after 1m51s
deploy
2026-01-28 19:05:20 +01:00

7.8 KiB

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
# 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
# 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:

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 <noreply@klz-cables.com>
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:

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:

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:

# 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:

    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:

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:

# 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 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! 🎉