Files
klz-cables.com/ENV_CLEANUP_SUMMARY.md
Marc Mintel 4e50482769
Some checks failed
Build & Deploy KLZ Cables / build-and-deploy (push) Failing after 5m50s
remove redis
2026-01-29 02:23:41 +01:00

10 KiB

Environment Variables Cleanup - Summary

What Was Done

Cleaned up the fragile, overkill environment variable mess and replaced it with a simple, clean, robust fully automated system.

Changes Made

1. Dockerfile

Before: 4 build args including runtime-only variables (SENTRY_DSN) After: 3 build args - only NEXT_PUBLIC_* variables that need to be baked into the client bundle

# Only these build args now:
ARG NEXT_PUBLIC_BASE_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL

2. docker-compose.yml

Before: 12+ individual environment variables listed After: Single env_file: .env directive

app:
  image: registry.infra.mintel.me/mintel/klz-cables.com:latest
  env_file:
    - .env  # All runtime vars loaded from here

3. .gitea/workflows/deploy.yml

Before: Passing 12+ environment variables individually via SSH command (fragile!) After: Fully automated - workflow creates .env file from Gitea secrets and uploads it

# Before (FRAGILE):
ssh root@alpha.mintel.me \
  "MAIL_FROM='${{ secrets.MAIL_FROM }}' \
   MAIL_HOST='${{ secrets.MAIL_HOST }}' \
   ... (12+ variables) \
   /home/deploy/deploy.sh"

# After (AUTOMATED):
# 1. Create .env from secrets
cat > /tmp/klz-cables.env << EOF
NODE_ENV=production
NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }}
# ... all other vars from secrets
EOF

# 2. Upload to server
scp /tmp/klz-cables.env root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/.env

# 3. Deploy
ssh root@alpha.mintel.me "cd /home/deploy/sites/klz-cables.com && docker-compose up -d"

4. New Files Created

  • .env.production - Template for reference (not used in automation)
  • docs/DEPLOYMENT.md - Complete deployment guide
  • docs/SERVER_SETUP.md - Server setup instructions
  • docs/ENV_MIGRATION.md - Migration guide from old to new system

5. Updated Files

  • .env.example - Clear documentation of all variables with build-time vs runtime notes

Architecture

Build Time (CI/CD)

Gitea Workflow
  ↓
  Only passes NEXT_PUBLIC_* as --build-arg
  ↓
Docker Build
  ↓
  Validates env vars
  ↓
  Bakes NEXT_PUBLIC_* into client bundle
  ↓
Push to Registry

Runtime (Production Server) - FULLY AUTOMATED

Gitea Secrets
  ↓
  Workflow creates .env file
  ↓
  SCP uploads to server
  ↓
  Secured (chmod 600, chown deploy:deploy)
  ↓
docker-compose.yml (env_file: .env)
  ↓
  Loads .env into container
  ↓
Application runs with full config

Key Benefits

1. Simplicity

  • Before: 15+ Gitea secrets, variables in 3+ places
  • After: All secrets in Gitea, automatically deployed

2. Clarity

  • Before: Confusing duplication, unclear which vars go where
  • After: Clear separation - build args vs runtime env file

3. Robustness

  • Before: Fragile SSH command with 12+ inline variables
  • After: Robust automated file generation and upload

4. Security

  • Before: Secrets potentially exposed in CI logs
  • After: Secrets masked in logs, .env auto-secured on server

5. Maintainability

  • Before: Update in 3 places (Dockerfile, docker-compose.yml, deploy.yml)
  • After: Update Gitea secrets only - deployment is automatic

6. Zero Manual Steps 🎉

  • Before: Manual .env file creation on server (error-prone, can be forgotten)
  • After: Fully automated - .env file created and uploaded on every deployment

What You Need to Do

Required Gitea Secrets

Ensure these secrets are configured in your Gitea repository:

Build-Time (NEXT_PUBLIC_*):

  • NEXT_PUBLIC_BASE_URL - Production URL (e.g., https://klz-cables.com)
  • NEXT_PUBLIC_UMAMI_WEBSITE_ID - Umami analytics ID
  • NEXT_PUBLIC_UMAMI_SCRIPT_URL - Umami script URL

Runtime:

  • SENTRY_DSN - Error tracking DSN
  • MAIL_HOST - SMTP server
  • MAIL_PORT - SMTP port (e.g., 587)
  • MAIL_USERNAME - SMTP username
  • MAIL_PASSWORD - SMTP password
  • MAIL_FROM - Sender email
  • MAIL_RECIPIENTS - Recipient emails (comma-separated)

Infrastructure:

  • REGISTRY_USER - Docker registry username
  • REGISTRY_PASS - Docker registry password
  • ALPHA_SSH_KEY - SSH private key for deployment server

Notifications:

  • GOTIFY_URL - Gotify notification server URL
  • GOTIFY_TOKEN - Gotify application token

That's It!

No manual steps required. Just push to main branch and the workflow will:

  1. Build Docker image with NEXT_PUBLIC_* build args
  2. Create .env file from all secrets
  3. Upload .env to server
  4. Secure .env file (600 permissions, deploy:deploy ownership)
  5. Pull latest image
  6. Deploy with docker-compose

Files Changed

Modified:
  ├── Dockerfile                      (removed redundant build args)
  ├── docker-compose.yml              (use env_file instead of individual vars)
  ├── .gitea/workflows/deploy.yml     (automated .env creation & upload)
  ├── .env.example                    (clear documentation)
  ├── lib/services/create-services.ts (removed redundant dotenv usage)
  └── scripts/migrate-*.ts            (removed redundant dotenv usage)

Created:
  ├── .env.production                 (reference template)
  ├── docs/DEPLOYMENT.md              (deployment guide)
  ├── docs/SERVER_SETUP.md            (server setup guide)
  ├── docs/ENV_MIGRATION.md           (migration guide)
  └── ENV_CLEANUP_SUMMARY.md          (this file)

Deployment Flow

┌─────────────────────────────────────────────────────────────┐
│ Developer pushes to main branch                             │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ Gitea Workflow Triggered                                    │
│                                                             │
│ 1. Build Docker image (NEXT_PUBLIC_* build args)           │
│ 2. Push to registry                                        │
│ 3. Generate .env from secrets                              │
│ 4. Upload .env to server via SCP                           │
│ 5. SSH to server and deploy                                │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ Production Server                                           │
│                                                             │
│ 1. .env file secured (600, deploy:deploy)                  │
│ 2. Docker login to registry                                │
│ 3. Pull latest image                                       │
│ 4. docker-compose down                                     │
│ 5. docker-compose up -d (loads .env)                       │
│ 6. Health checks pass                                      │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ ✅ Deployment Complete - Gotify Notification Sent          │
└─────────────────────────────────────────────────────────────┘

Comparison: Before vs After

Aspect Before After
Gitea Secrets 15+ secrets Same secrets, better organized
Build Args 4 vars (including runtime-only) 3 vars (NEXT_PUBLIC_* only)
Runtime Vars Passed via SSH command Auto-generated .env file
Manual Steps Manual .env creation Fully automated
Maintenance Update in 3 places Update Gitea secrets only
Security Secrets in CI logs Secrets masked, .env secured
Clarity Confusing duplication Clear separation
Robustness Fragile SSH command Robust automation
Error-Prone Can forget .env Impossible to forget

Documentation

Troubleshooting

Deployment Fails

  1. Check Gitea secrets - Ensure all required secrets are set
  2. Check workflow logs - Look for specific error messages
  3. SSH to server - Verify .env file exists and has correct permissions
  4. Check container logs - docker-compose logs -f app

.env File Issues

The workflow automatically:

  • Creates .env from secrets
  • Uploads to server
  • Sets 600 permissions
  • Sets deploy:deploy ownership

If there are issues, check the workflow logs for the "📝 Preparing environment configuration" step.

Missing Environment Variables

If a variable is missing:

  1. Add it to Gitea secrets
  2. Update .gitea/workflows/deploy.yml to include it in the .env generation
  3. Push to trigger new deployment

Result: Environment variable management is now simple, clean, robust, and fully automated! 🎉

No more manual .env file creation. No more forgotten configuration. No more fragile SSH commands. Just push and deploy!