This commit is contained in:
86
.env.example
86
.env.example
@@ -1,24 +1,33 @@
|
||||
# WooCommerce API (Legacy - not currently used)
|
||||
WOOCOMMERCE_URL=https://klz-cables.com
|
||||
WOOCOMMERCE_CONSUMER_KEY=
|
||||
WOOCOMMERCE_CONSUMER_SECRET=
|
||||
WORDPRESS_APP_PASSWORD=
|
||||
# ============================================================================
|
||||
# KLZ Cables - Environment Configuration
|
||||
# ============================================================================
|
||||
# Copy this file to .env for local development
|
||||
# For production, use .env.production as a template
|
||||
# ============================================================================
|
||||
|
||||
# Umami Analytics
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
||||
UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
|
||||
|
||||
# GlitchTip (Sentry protocol)
|
||||
SENTRY_DSN=
|
||||
# Client-side DSN should use the proxy path: https://[key]@[domain]/errors/[id]
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
|
||||
# Application
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Application Configuration
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
NODE_ENV=development
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||
LOG_LEVEL=info
|
||||
|
||||
# SMTP Configuration
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Analytics (Umami)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Optional: Leave empty to disable analytics
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Error Tracking (GlitchTip/Sentry)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Optional: Leave empty to disable error tracking
|
||||
SENTRY_DSN=
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Email Configuration (SMTP)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Required for contact form functionality
|
||||
MAIL_HOST=smtp.eu.mailgun.org
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=
|
||||
@@ -26,6 +35,45 @@ MAIL_PASSWORD=
|
||||
MAIL_FROM=KLZ Cables <noreply@klz-cables.com>
|
||||
MAIL_RECIPIENTS=info@klz-cables.com
|
||||
|
||||
# Redis Configuration (optional)
|
||||
REDIS_URL=redis://redis:6379/2
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Redis Cache Configuration
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Optional: Leave empty to disable Redis caching
|
||||
REDIS_URL=redis://localhost:6379/2
|
||||
REDIS_KEY_PREFIX=klz:
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Logging
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
LOG_LEVEL=info
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Varnish Cache (Docker only)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
VARNISH_CACHE_SIZE=256m
|
||||
|
||||
# ============================================================================
|
||||
# IMPORTANT NOTES
|
||||
# ============================================================================
|
||||
#
|
||||
# BUILD-TIME vs RUNTIME Variables:
|
||||
# ─────────────────────────────────
|
||||
# • NEXT_PUBLIC_* variables are baked into the client bundle at BUILD time
|
||||
# They must be provided as --build-arg when building the Docker image
|
||||
#
|
||||
# • All other variables are used at RUNTIME only
|
||||
# They are loaded from the .env file by docker-compose
|
||||
#
|
||||
# Docker Deployment:
|
||||
# ──────────────────
|
||||
# 1. Build-time: Only NEXT_PUBLIC_* vars are needed (via --build-arg)
|
||||
# 2. Runtime: All vars are loaded from .env file on the server
|
||||
# 3. The .env file should exist at: /home/deploy/sites/klz-cables.com/.env
|
||||
#
|
||||
# Security:
|
||||
# ─────────
|
||||
# • NEVER commit .env files with real credentials to git
|
||||
# • Use Gitea/GitHub secrets for CI/CD workflows
|
||||
# • Store production .env file securely on the server only
|
||||
#
|
||||
# ============================================================================
|
||||
|
||||
34
.env.production
Normal file
34
.env.production
Normal file
@@ -0,0 +1,34 @@
|
||||
# ============================================================================
|
||||
# KLZ Cables - Production Environment Configuration
|
||||
# ============================================================================
|
||||
# This file contains runtime environment variables for the production deployment.
|
||||
# It should be placed on the production server at: /home/deploy/sites/klz-cables.com/.env
|
||||
#
|
||||
# IMPORTANT: This file contains sensitive data and should NEVER be committed to git.
|
||||
# ============================================================================
|
||||
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
NEXT_PUBLIC_BASE_URL=https://klz-cables.com
|
||||
|
||||
# Analytics (Umami)
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
|
||||
|
||||
# Error Tracking (GlitchTip/Sentry)
|
||||
SENTRY_DSN=
|
||||
|
||||
# Email Configuration (Mailgun)
|
||||
MAIL_HOST=smtp.eu.mailgun.org
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_FROM=KLZ Cables <noreply@klz-cables.com>
|
||||
MAIL_RECIPIENTS=info@klz-cables.com
|
||||
|
||||
# Redis Cache (optional)
|
||||
REDIS_URL=redis://redis:6379/2
|
||||
REDIS_KEY_PREFIX=klz:
|
||||
|
||||
# Varnish Cache Size (optional)
|
||||
VARNISH_CACHE_SIZE=256m
|
||||
@@ -6,8 +6,6 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
# ────────────────────────────────────────────────
|
||||
# WICHTIG: Kein "docker" mehr – sondern eines der neuen Labels
|
||||
runs-on: docker
|
||||
|
||||
steps:
|
||||
@@ -72,11 +70,10 @@ jobs:
|
||||
echo " Platform: linux/arm64"
|
||||
echo " Target: registry.infra.mintel.me/mintel/klz-cables.com:latest"
|
||||
echo ""
|
||||
echo "📦 Build Arguments:"
|
||||
echo "📦 Build Arguments (NEXT_PUBLIC_* only - baked into client bundle):"
|
||||
echo " • NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL != '' && '***' || 'NOT SET' }}"
|
||||
echo " • NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID != '' && '***' || 'NOT SET' }}"
|
||||
echo " • NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL != '' && '***' || 'NOT SET' }}"
|
||||
echo " • SENTRY_DSN: ${{ secrets.SENTRY_DSN != '' && '***' || 'NOT SET' }}"
|
||||
echo " • NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL != '' && '***' || 'NOT SET' }}"
|
||||
echo ""
|
||||
echo "⏱️ Build started at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo ""
|
||||
@@ -86,10 +83,9 @@ jobs:
|
||||
docker buildx build \
|
||||
--pull \
|
||||
--platform linux/arm64 \
|
||||
--build-arg NEXT_PUBLIC_BASE_URL="${{ secrets.NEXT_PUBLIC_BASE_URL }}" \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}" \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}" \
|
||||
--build-arg SENTRY_DSN="${{ secrets.SENTRY_DSN }}" \
|
||||
--build-arg NEXT_PUBLIC_BASE_URL="${{ secrets.NEXT_PUBLIC_BASE_URL }}" \
|
||||
-t registry.infra.mintel.me/mintel/klz-cables.com:latest \
|
||||
--push .
|
||||
|
||||
@@ -141,36 +137,112 @@ jobs:
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Create .env file content
|
||||
echo "📝 Preparing environment configuration..."
|
||||
cat > /tmp/klz-cables.env << EOF
|
||||
# ============================================================================
|
||||
# KLZ Cables - Production Environment Configuration
|
||||
# ============================================================================
|
||||
# Auto-generated by CI/CD workflow
|
||||
# DO NOT EDIT MANUALLY - Changes will be overwritten on next deployment
|
||||
# ============================================================================
|
||||
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }}
|
||||
|
||||
# Analytics (Umami)
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL=${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}
|
||||
|
||||
# Error Tracking (GlitchTip/Sentry)
|
||||
SENTRY_DSN=${{ secrets.SENTRY_DSN }}
|
||||
|
||||
# Email Configuration (Mailgun)
|
||||
MAIL_HOST=${{ secrets.MAIL_HOST }}
|
||||
MAIL_PORT=${{ secrets.MAIL_PORT }}
|
||||
MAIL_USERNAME=${{ secrets.MAIL_USERNAME }}
|
||||
MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }}
|
||||
MAIL_FROM=${{ secrets.MAIL_FROM }}
|
||||
MAIL_RECIPIENTS=${{ secrets.MAIL_RECIPIENTS }}
|
||||
|
||||
# Redis Cache
|
||||
REDIS_URL=${{ secrets.REDIS_URL }}
|
||||
REDIS_KEY_PREFIX=${{ secrets.REDIS_KEY_PREFIX }}
|
||||
|
||||
# Varnish Cache Size
|
||||
VARNISH_CACHE_SIZE=256m
|
||||
EOF
|
||||
|
||||
echo "✅ Environment file prepared"
|
||||
echo ""
|
||||
|
||||
# Execute deployment commands with detailed logging
|
||||
echo "📡 Connecting to server and executing deployment commands..."
|
||||
echo "📡 Connecting to server and executing deployment..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# SSH as root and use sudo to run deployment script as deploy user
|
||||
# This works around the broken SSH output issue with deploy user
|
||||
# Copy .env file to server
|
||||
echo "📤 Uploading environment configuration..."
|
||||
scp -o StrictHostKeyChecking=accept-new \
|
||||
-o ServerAliveInterval=30 \
|
||||
-o ServerAliveCountMax=3 \
|
||||
-o ConnectTimeout=10 \
|
||||
/tmp/klz-cables.env \
|
||||
root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Environment file uploaded successfully"
|
||||
else
|
||||
echo "❌ Failed to upload environment file"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# SSH to server and run deployment
|
||||
echo "🚀 Executing deployment on server..."
|
||||
ssh -o StrictHostKeyChecking=accept-new \
|
||||
-o ServerAliveInterval=30 \
|
||||
-o ServerAliveCountMax=3 \
|
||||
-o ConnectTimeout=10 \
|
||||
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 }}' \
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID='${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}' \
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL='${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}' \
|
||||
NODE_ENV='${{ secrets.NODE_ENV }}' \
|
||||
SENTRY_DSN='${{ secrets.SENTRY_DSN }}' \
|
||||
REDIS_URL='${{ secrets.REDIS_URL }}' \
|
||||
REDIS_KEY_PREFIX='${{ secrets.REDIS_KEY_PREFIX }}' \
|
||||
/home/deploy/deploy.sh"
|
||||
root@alpha.mintel.me bash << EOF
|
||||
set -e
|
||||
|
||||
PROJECT_DIR="/home/deploy/sites/klz-cables.com"
|
||||
cd "\$PROJECT_DIR"
|
||||
|
||||
echo "🔒 Securing environment file..."
|
||||
chmod 600 .env
|
||||
chown deploy:deploy .env
|
||||
|
||||
echo "🔐 Logging into Docker registry..."
|
||||
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.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
|
||||
|
||||
DEPLOY_EXIT_CODE=$?
|
||||
echo ""
|
||||
|
||||
# Clean up temporary env file
|
||||
rm -f /tmp/klz-cables.env
|
||||
|
||||
if [ $DEPLOY_EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ Deployment completed successfully at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
|
||||
else
|
||||
@@ -181,6 +253,7 @@ jobs:
|
||||
echo " • Verify SSH key permissions on server"
|
||||
echo " • Check disk space on target server"
|
||||
echo " • Review docker compose configuration"
|
||||
echo " • Verify all required secrets are set in Gitea"
|
||||
exit $DEPLOY_EXIT_CODE
|
||||
fi
|
||||
echo ""
|
||||
@@ -208,6 +281,8 @@ jobs:
|
||||
echo " • All secrets are masked (*** ) in logs"
|
||||
echo " • SSH keys are created with 600 permissions"
|
||||
echo " • Passwords are never displayed in plain text"
|
||||
echo " • .env file is auto-generated from Gitea secrets"
|
||||
echo " • .env file has 600 permissions on server"
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
|
||||
if [ "${{ job.status }}" == "success" ]; then
|
||||
|
||||
@@ -22,15 +22,15 @@ COPY . .
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# Build-time environment variables for Next.js
|
||||
# These are baked into the client bundle during build
|
||||
ARG NEXT_PUBLIC_BASE_URL
|
||||
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
||||
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
|
||||
ARG SENTRY_DSN
|
||||
ARG NEXT_PUBLIC_BASE_URL
|
||||
|
||||
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
|
||||
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
||||
ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
|
||||
ENV SENTRY_DSN=$SENTRY_DSN
|
||||
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
|
||||
|
||||
# Validate environment variables during build
|
||||
RUN npx tsx scripts/validate-env.ts
|
||||
|
||||
272
ENV_CLEANUP_SUMMARY.md
Normal file
272
ENV_CLEANUP_SUMMARY.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 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
|
||||
|
||||
```dockerfile
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```yaml
|
||||
# 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)
|
||||
- `REDIS_URL` - Redis connection URL
|
||||
- `REDIS_KEY_PREFIX` - Redis key prefix (e.g., `klz:`)
|
||||
|
||||
**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)
|
||||
|
||||
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
|
||||
|
||||
- **[DEPLOYMENT.md](docs/DEPLOYMENT.md)** - Complete deployment guide
|
||||
- **[SERVER_SETUP.md](docs/SERVER_SETUP.md)** - Server setup instructions (mostly automated now)
|
||||
- **[ENV_MIGRATION.md](docs/ENV_MIGRATION.md)** - Migration from old to new system
|
||||
- **[.env.example](.env.example)** - Environment variables reference
|
||||
- **[.env.production](.env.production)** - Production template (for reference)
|
||||
|
||||
## 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!
|
||||
@@ -50,26 +50,14 @@ services:
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
env_file:
|
||||
- .env
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:3000/health || wget --quiet --tries=1 --spider http://localhost:3000/ || true"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
environment:
|
||||
- NODE_ENV=${NODE_ENV:-production}
|
||||
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:?NEXT_PUBLIC_BASE_URL is required}
|
||||
- NEXT_PUBLIC_UMAMI_WEBSITE_ID=${NEXT_PUBLIC_UMAMI_WEBSITE_ID}
|
||||
- NEXT_PUBLIC_UMAMI_SCRIPT_URL=${NEXT_PUBLIC_UMAMI_SCRIPT_URL}
|
||||
- SENTRY_DSN=${SENTRY_DSN}
|
||||
- MAIL_HOST=${MAIL_HOST}
|
||||
- MAIL_PORT=${MAIL_PORT:-587}
|
||||
- MAIL_USERNAME=${MAIL_USERNAME}
|
||||
- MAIL_PASSWORD=${MAIL_PASSWORD}
|
||||
- MAIL_FROM=${MAIL_FROM}
|
||||
- MAIL_RECIPIENTS=${MAIL_RECIPIENTS}
|
||||
- REDIS_URL=${REDIS_URL}
|
||||
- REDIS_KEY_PREFIX=${REDIS_KEY_PREFIX}
|
||||
|
||||
networks:
|
||||
infra:
|
||||
|
||||
@@ -1,214 +1,278 @@
|
||||
# Deployment Guide
|
||||
# KLZ Cables - Deployment Guide
|
||||
|
||||
This document describes the deployment setup for KLZ Cables website.
|
||||
This document explains the deployment process and environment variable management for the KLZ Cables application.
|
||||
|
||||
## Automatic Deployment (Gitea Actions)
|
||||
## Table of Contents
|
||||
|
||||
The project uses Gitea Actions for CI/CD. On every push to the `main` branch:
|
||||
- [Overview](#overview)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [Local Development](#local-development)
|
||||
- [Production Deployment](#production-deployment)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
1. **Build**: Docker image is built with platform `linux/arm64`
|
||||
2. **Push**: Image is pushed to `registry.infra.mintel.me/mintel/klz-cables.com:latest`
|
||||
3. **Deploy**: SSH connection to production server pulls and restarts containers
|
||||
## Overview
|
||||
|
||||
### Workflow File
|
||||
The application uses a clean, robust, **fully automated** environment variable strategy:
|
||||
|
||||
Location: `.gitea/workflows/deploy.yml`
|
||||
- **Build-time variables**: Only `NEXT_PUBLIC_*` variables are baked into the client bundle
|
||||
- **Runtime variables**: All other variables are loaded from `.env` file at runtime
|
||||
- **Fully automated**: `.env` file is automatically generated from Gitea secrets on every deployment
|
||||
- **No manual steps**: No need to manually create or update `.env` files on the server
|
||||
- **No duplication**: Single source of truth for each environment
|
||||
|
||||
### Required Secrets
|
||||
### Architecture
|
||||
|
||||
Configure these in your Gitea repository settings:
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CI/CD Workflow (.gitea/workflows/deploy.yml) │
|
||||
│ │
|
||||
│ 1. Build Docker image with NEXT_PUBLIC_* build args │
|
||||
│ 2. Push to registry │
|
||||
│ 3. SSH to server and run deploy.sh │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Production Server (alpha.mintel.me) │
|
||||
│ │
|
||||
│ /home/deploy/sites/klz-cables.com/ │
|
||||
│ ├── .env ← Runtime environment vars │
|
||||
│ ├── docker-compose.yml ← Loads .env file │
|
||||
│ └── varnish/ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- `REGISTRY_USER` - Docker registry username
|
||||
- `REGISTRY_PASS` - Docker registry password
|
||||
- `ALPHA_SSH_KEY` - SSH private key for deployment user
|
||||
- `NEXT_PUBLIC_UMAMI_WEBSITE_ID` - Umami analytics website ID
|
||||
- `NEXT_PUBLIC_UMAMI_SCRIPT_URL` - Umami analytics script URL
|
||||
- `SENTRY_DSN` - Sentry/GlitchTip DSN for error tracking
|
||||
## Environment Variables
|
||||
|
||||
## Manual Deployment
|
||||
### Build-Time Variables (NEXT_PUBLIC_*)
|
||||
|
||||
If you need to deploy manually:
|
||||
These are embedded into the JavaScript bundle during build and are visible to the client:
|
||||
|
||||
### On the Production Server
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `NEXT_PUBLIC_BASE_URL` | ✅ Yes | Base URL of the application (e.g., `https://klz-cables.com`) |
|
||||
| `NEXT_PUBLIC_UMAMI_WEBSITE_ID` | ❌ No | Umami analytics website ID |
|
||||
| `NEXT_PUBLIC_UMAMI_SCRIPT_URL` | ❌ No | Umami analytics script URL (default: `https://analytics.infra.mintel.me/script.js`) |
|
||||
|
||||
**Important**: These must be provided as `--build-arg` when building the Docker image.
|
||||
|
||||
### Runtime Variables
|
||||
|
||||
These are loaded from the `.env` file at runtime and are only available on the server:
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `NODE_ENV` | ✅ Yes | Environment mode (`production`, `development`, `test`) |
|
||||
| `SENTRY_DSN` | ❌ No | GlitchTip/Sentry error tracking DSN |
|
||||
| `MAIL_HOST` | ❌ No | SMTP server hostname |
|
||||
| `MAIL_PORT` | ❌ No | SMTP server port (default: `587`) |
|
||||
| `MAIL_USERNAME` | ❌ No | SMTP authentication username |
|
||||
| `MAIL_PASSWORD` | ❌ No | SMTP authentication password |
|
||||
| `MAIL_FROM` | ❌ No | Email sender address |
|
||||
| `MAIL_RECIPIENTS` | ❌ No | Comma-separated list of recipient emails |
|
||||
| `REDIS_URL` | ❌ No | Redis connection URL (e.g., `redis://redis:6379/2`) |
|
||||
| `REDIS_KEY_PREFIX` | ❌ No | Redis key prefix (default: `klz:`) |
|
||||
| `VARNISH_CACHE_SIZE` | ❌ No | Varnish cache size (default: `256m`) |
|
||||
|
||||
## Local Development
|
||||
|
||||
### Setup
|
||||
|
||||
1. Copy the example environment file:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Edit `.env` and fill in your local configuration:
|
||||
```bash
|
||||
NODE_ENV=development
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||
# Add other variables as needed
|
||||
```
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
4. Run the development server:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Testing Docker Build Locally
|
||||
|
||||
```bash
|
||||
# SSH into the server
|
||||
ssh deploy@alpha.mintel.me
|
||||
# Build with build-time arguments
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_BASE_URL=http://localhost:3000 \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID=your-id \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js \
|
||||
-t klz-cables:local .
|
||||
|
||||
# Run with runtime environment file
|
||||
docker run --env-file .env -p 3000:3000 klz-cables:local
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Server Setup**: Ensure the production server has:
|
||||
- Docker and Docker Compose installed
|
||||
- Traefik reverse proxy running
|
||||
- Network `infra` created
|
||||
- Project directory exists: `/home/deploy/sites/klz-cables.com`
|
||||
|
||||
2. **Gitea Secrets**: Configure the following secrets in your Gitea repository:
|
||||
|
||||
**Registry Access:**
|
||||
- `REGISTRY_USER` - Docker registry username
|
||||
- `REGISTRY_PASS` - Docker registry password
|
||||
|
||||
**Build-Time Variables:**
|
||||
- `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 Variables:**
|
||||
- `SENTRY_DSN` - Error tracking DSN
|
||||
- `MAIL_HOST` - SMTP server
|
||||
- `MAIL_PORT` - SMTP port
|
||||
- `MAIL_USERNAME` - SMTP username
|
||||
- `MAIL_PASSWORD` - SMTP password
|
||||
- `MAIL_FROM` - Sender email
|
||||
- `MAIL_RECIPIENTS` - Recipient emails (comma-separated)
|
||||
- `REDIS_URL` - Redis connection URL
|
||||
- `REDIS_KEY_PREFIX` - Redis key prefix
|
||||
|
||||
**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
|
||||
|
||||
### Deployment Process
|
||||
|
||||
The deployment is **fully automated** via Gitea Actions:
|
||||
|
||||
1. **Trigger**: Push to `main` branch
|
||||
2. **Build**: Docker image is built with `NEXT_PUBLIC_*` build arguments
|
||||
3. **Push**: Image is pushed to private registry
|
||||
4. **Generate .env**: Workflow creates `.env` file from all Gitea secrets
|
||||
5. **Upload .env**: File is uploaded to server via SCP and secured (600 permissions)
|
||||
6. **Deploy**: SSH to server, pull image, and run docker-compose
|
||||
7. **Notify**: Send success/failure notification via Gotify
|
||||
|
||||
**No manual steps required!** The `.env` file is automatically created and deployed on every push.
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
If you need to deploy manually (not recommended - use the automated workflow):
|
||||
|
||||
```bash
|
||||
# SSH to the server
|
||||
ssh root@alpha.mintel.me
|
||||
|
||||
# Navigate to the project directory
|
||||
cd /home/deploy/sites/klz-cables.com
|
||||
|
||||
# Ensure .env file exists (normally created by workflow)
|
||||
# If missing, you'll need to create it from Gitea secrets
|
||||
|
||||
# Pull the latest image
|
||||
docker compose pull
|
||||
docker pull registry.infra.mintel.me/mintel/klz-cables.com:latest
|
||||
|
||||
# Restart containers
|
||||
docker compose up -d --force-recreate --remove-orphans
|
||||
# Restart the services
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
|
||||
# Clean up old images
|
||||
docker image prune -f
|
||||
# Check logs
|
||||
docker-compose logs -f app
|
||||
```
|
||||
|
||||
**Note**: Manual deployment requires the `.env` file to exist. The automated workflow creates this file from Gitea secrets. If deploying manually, you'll need to create it yourself using [`.env.production`](.env.production) as a template.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Workflow Not Triggering
|
||||
|
||||
1. Check Gitea Actions is enabled in repository settings
|
||||
2. Verify the workflow file syntax
|
||||
3. Check runner availability with label `docker`
|
||||
|
||||
### Build Failures
|
||||
|
||||
1. Check build logs in Gitea Actions tab
|
||||
2. Verify all secrets are configured correctly
|
||||
3. Ensure Dockerfile is valid
|
||||
|
||||
### Deployment Failures
|
||||
|
||||
1. Verify SSH key has correct permissions (600)
|
||||
2. Check deploy user has Docker permissions
|
||||
3. Verify registry credentials are correct
|
||||
4. Check server disk space: `df -h`
|
||||
|
||||
### Container Issues
|
||||
**Problem**: Build fails with "Environment validation failed"
|
||||
|
||||
**Solution**: Ensure all required `NEXT_PUBLIC_*` variables are provided as build arguments:
|
||||
```bash
|
||||
# Check container status
|
||||
docker compose ps
|
||||
|
||||
# View logs
|
||||
docker compose logs -f app
|
||||
docker compose logs -f varnish
|
||||
|
||||
# Check health
|
||||
docker compose exec app wget -O- http://localhost:3000/health
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_BASE_URL=https://klz-cables.com \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID=your-id \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js \
|
||||
-t klz-cables .
|
||||
```
|
||||
|
||||
## Architecture
|
||||
### Runtime Failures
|
||||
|
||||
```
|
||||
Client
|
||||
↓
|
||||
Traefik (TLS termination, routing)
|
||||
↓
|
||||
Varnish (HTTP caching)
|
||||
↓
|
||||
Next.js App (port 3000)
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
- **app**: Next.js application
|
||||
- **varnish**: HTTP cache layer
|
||||
- **traefik**: Reverse proxy (external network)
|
||||
|
||||
### Domains
|
||||
|
||||
- `klz-cables.com` - Production
|
||||
- `www.klz-cables.com` - Production (www)
|
||||
- `staging.klz-cables.com` - Staging
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Build-time (in Dockerfile/Workflow)
|
||||
|
||||
These variables are baked into the Docker image during the build process.
|
||||
|
||||
- `NEXT_PUBLIC_BASE_URL` (Required)
|
||||
- `NEXT_PUBLIC_UMAMI_WEBSITE_ID`
|
||||
- `NEXT_PUBLIC_UMAMI_SCRIPT_URL`
|
||||
- `SENTRY_DSN`
|
||||
|
||||
### Runtime (in docker-compose.yml)
|
||||
|
||||
These variables are passed to the container at runtime.
|
||||
|
||||
- `NODE_ENV` (Defaults to `production`)
|
||||
- `NEXT_PUBLIC_BASE_URL` (Must be passed again at runtime for server-side use)
|
||||
- `SENTRY_DSN`
|
||||
- `MAIL_HOST`
|
||||
- `MAIL_PORT`
|
||||
- `MAIL_USERNAME`
|
||||
- `MAIL_PASSWORD`
|
||||
- `MAIL_FROM`
|
||||
- `MAIL_RECIPIENTS`
|
||||
- `REDIS_URL`
|
||||
- `REDIS_KEY_PREFIX`
|
||||
|
||||
### Local Development
|
||||
|
||||
For local development, use a `.env` file. The `docker-compose.override.yml` file automatically loads this file for the `app` service.
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Health Checks
|
||||
|
||||
- App: `https://klz-cables.com/health`
|
||||
- Varnish: Configured in docker-compose.yml
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# Application logs
|
||||
docker compose logs -f app
|
||||
|
||||
# Varnish logs
|
||||
docker compose logs -f varnish
|
||||
|
||||
# All logs
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
### Analytics
|
||||
|
||||
- Umami: Configured via environment variables
|
||||
- Sentry/GlitchTip: Error tracking
|
||||
|
||||
## Rollback
|
||||
|
||||
To rollback to a previous version:
|
||||
**Problem**: Container starts but application crashes
|
||||
|
||||
**Solution**: Check that the `.env` file exists and contains all required runtime variables:
|
||||
```bash
|
||||
# On the server
|
||||
cd /home/deploy/sites/klz-cables.com
|
||||
cat /home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
# Pull a specific version (if tagged)
|
||||
docker pull registry.infra.mintel.me/mintel/klz-cables.com:TAG
|
||||
|
||||
# Or rebuild from a specific commit
|
||||
# (requires access to the repository on the server)
|
||||
|
||||
# Restart with the older image
|
||||
docker compose up -d --force-recreate
|
||||
# Check container logs
|
||||
docker-compose logs app
|
||||
```
|
||||
|
||||
## Performance
|
||||
### Missing Environment Variables
|
||||
|
||||
### Cache Invalidation
|
||||
**Problem**: Features not working (email, analytics, etc.)
|
||||
|
||||
Varnish caches static assets. To clear cache:
|
||||
**Solution**:
|
||||
1. Check that the secret is configured in Gitea
|
||||
2. Verify the workflow includes it in the `.env` generation (see `.gitea/workflows/deploy.yml`)
|
||||
3. Redeploy to regenerate the `.env` file:
|
||||
```bash
|
||||
git commit --allow-empty -m "Trigger redeploy"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
4. Check the variable is loaded on the server:
|
||||
```bash
|
||||
ssh root@alpha.mintel.me
|
||||
cd /home/deploy/sites/klz-cables.com
|
||||
docker-compose exec app env | grep VARIABLE_NAME
|
||||
```
|
||||
|
||||
### Docker Compose Issues
|
||||
|
||||
**Problem**: `docker-compose up` fails with "env file not found"
|
||||
|
||||
**Solution**: The `.env` file should be automatically created by the workflow. If it's missing:
|
||||
1. Check the workflow logs for errors in the "📝 Preparing environment configuration" step
|
||||
2. Manually trigger a deployment by pushing to main
|
||||
3. If still missing, check server permissions and disk space
|
||||
|
||||
### Network Issues
|
||||
|
||||
**Problem**: Container can't connect to Traefik
|
||||
|
||||
**Solution**: Verify the `infra` network exists:
|
||||
```bash
|
||||
docker compose exec varnish varnishadm "ban req.url ~ ."
|
||||
docker network ls | grep infra
|
||||
docker network inspect infra
|
||||
```
|
||||
|
||||
### Cache Configuration
|
||||
## Security Best Practices
|
||||
|
||||
Edit `varnish/default.vcl` and restart:
|
||||
1. **Never commit `.env` files** with real credentials to git
|
||||
2. **Use Gitea/GitHub secrets** for CI/CD workflows
|
||||
3. **Restrict SSH key access** to deployment server
|
||||
4. **Rotate credentials regularly** (SMTP passwords, API keys, etc.)
|
||||
5. **Use HTTPS** for all production URLs
|
||||
6. **Monitor logs** for suspicious activity
|
||||
|
||||
```bash
|
||||
docker compose restart varnish
|
||||
```
|
||||
## Additional Resources
|
||||
|
||||
## Security
|
||||
|
||||
- All secrets are stored in Gitea repository settings
|
||||
- SSH key is injected at deployment time
|
||||
- Registry credentials are not stored in the repository
|
||||
- Deploy webhook requires secret token
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check logs first
|
||||
2. Review this documentation
|
||||
3. Contact the development team
|
||||
- [Next.js Environment Variables](https://nextjs.org/docs/basic-features/environment-variables)
|
||||
- [Docker Compose Environment Variables](https://docs.docker.com/compose/environment-variables/)
|
||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||
|
||||
276
docs/ENV_MIGRATION.md
Normal file
276
docs/ENV_MIGRATION.md
Normal file
@@ -0,0 +1,276 @@
|
||||
# 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 <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:
|
||||
|
||||
```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! 🎉
|
||||
349
docs/SERVER_SETUP.md
Normal file
349
docs/SERVER_SETUP.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# Server Setup Guide - KLZ Cables
|
||||
|
||||
This guide explains how to set up the production environment on the deployment server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Server: `alpha.mintel.me`
|
||||
- User: `deploy` (with sudo access)
|
||||
- Docker and Docker Compose installed
|
||||
- Traefik reverse proxy running
|
||||
- External network `infra` created
|
||||
|
||||
## Initial Server Setup
|
||||
|
||||
### 1. Create Project Directory
|
||||
|
||||
```bash
|
||||
# SSH to the server as root or deploy user
|
||||
ssh root@alpha.mintel.me
|
||||
|
||||
# Create project directory
|
||||
mkdir -p /home/deploy/sites/klz-cables.com
|
||||
cd /home/deploy/sites/klz-cables.com
|
||||
```
|
||||
|
||||
### 2. Create Environment File
|
||||
|
||||
Create the `.env` file with production configuration:
|
||||
|
||||
```bash
|
||||
# Create .env file from template
|
||||
cat > /home/deploy/sites/klz-cables.com/.env << 'EOF'
|
||||
# ============================================================================
|
||||
# KLZ Cables - Production Environment Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
NEXT_PUBLIC_BASE_URL=https://klz-cables.com
|
||||
|
||||
# Analytics (Umami)
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=your-umami-website-id
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
|
||||
|
||||
# Error Tracking (GlitchTip/Sentry)
|
||||
SENTRY_DSN=https://your-sentry-dsn@errors.infra.mintel.me/project-id
|
||||
|
||||
# Email Configuration (Mailgun)
|
||||
MAIL_HOST=smtp.eu.mailgun.org
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=your-mailgun-username
|
||||
MAIL_PASSWORD=your-mailgun-password
|
||||
MAIL_FROM=KLZ Cables <noreply@klz-cables.com>
|
||||
MAIL_RECIPIENTS=info@klz-cables.com
|
||||
|
||||
# Redis Cache
|
||||
REDIS_URL=redis://redis:6379/2
|
||||
REDIS_KEY_PREFIX=klz:
|
||||
|
||||
# Varnish Cache Size
|
||||
VARNISH_CACHE_SIZE=256m
|
||||
EOF
|
||||
```
|
||||
|
||||
**Important**: Replace all placeholder values with actual production credentials.
|
||||
|
||||
### 3. Secure the Environment File
|
||||
|
||||
```bash
|
||||
# Set proper permissions (readable only by owner)
|
||||
chmod 600 /home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
# Verify ownership
|
||||
chown deploy:deploy /home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
# Verify permissions
|
||||
ls -la /home/deploy/sites/klz-cables.com/.env
|
||||
# Should show: -rw------- 1 deploy deploy
|
||||
```
|
||||
|
||||
### 4. Create Docker Compose File
|
||||
|
||||
```bash
|
||||
# Copy docker-compose.yml from repository
|
||||
# This should be done automatically by the deployment script
|
||||
# Or manually:
|
||||
cat > /home/deploy/sites/klz-cables.com/docker-compose.yml << 'EOF'
|
||||
services:
|
||||
varnish:
|
||||
image: varnish:7
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
depends_on:
|
||||
- app
|
||||
command: >-
|
||||
varnishd
|
||||
-F
|
||||
-f /etc/varnish/default.vcl
|
||||
-s malloc,${VARNISH_CACHE_SIZE:-256m}
|
||||
volumes:
|
||||
- ./varnish/default.vcl:/etc/varnish/default.vcl:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:80/health || wget --quiet --tries=1 --spider http://localhost:80/ || true"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.klz-cables-web.rule=(Host(\`klz-cables.com\`) || Host(\`www.klz-cables.com\`) || Host(\`staging.klz-cables.com\`)) && !PathPrefix(\`/.well-known/acme-challenge/\`)"
|
||||
- "traefik.http.routers.klz-cables-web.entrypoints=web"
|
||||
- "traefik.http.routers.klz-cables-web.middlewares=redirect-https"
|
||||
- "traefik.http.routers.klz-cables.rule=Host(\`klz-cables.com\`) || Host(\`www.klz-cables.com\`) || Host(\`staging.klz-cables.com\`)"
|
||||
- "traefik.http.routers.klz-cables.entrypoints=websecure"
|
||||
- "traefik.http.routers.klz-cables.tls.certresolver=le"
|
||||
- "traefik.http.routers.klz-cables.tls=true"
|
||||
- "traefik.http.routers.klz-cables.service=klz-cables"
|
||||
- "traefik.http.services.klz-cables.loadbalancer.server.port=80"
|
||||
- "traefik.http.services.klz-cables.loadbalancer.server.scheme=http"
|
||||
- "traefik.http.middlewares.klz-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
|
||||
- "traefik.http.middlewares.klz-forward.headers.customrequestheaders.X-Forwarded-Ssl=on"
|
||||
- "traefik.http.routers.klz-cables.middlewares=klz-forward,compress"
|
||||
|
||||
app:
|
||||
image: registry.infra.mintel.me/mintel/klz-cables.com:latest
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
env_file:
|
||||
- .env
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:3000/health || wget --quiet --tries=1 --spider http://localhost:3000/ || true"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
infra:
|
||||
external: true
|
||||
EOF
|
||||
```
|
||||
|
||||
### 5. Create Varnish Configuration
|
||||
|
||||
```bash
|
||||
# Create varnish directory
|
||||
mkdir -p /home/deploy/sites/klz-cables.com/varnish
|
||||
|
||||
# Copy varnish configuration from repository
|
||||
# This should be in the repository at varnish/default.vcl
|
||||
```
|
||||
|
||||
### 6. Create Deployment Script
|
||||
|
||||
```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"
|
||||
echo "Please create the .env file before deploying."
|
||||
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!"
|
||||
echo ""
|
||||
echo "📊 Service URLs:"
|
||||
echo " • Production: https://klz-cables.com"
|
||||
echo " • Staging: https://staging.klz-cables.com"
|
||||
echo ""
|
||||
EOF
|
||||
|
||||
# Make script executable
|
||||
chmod +x /home/deploy/deploy.sh
|
||||
```
|
||||
|
||||
### 7. Configure Docker Registry Access
|
||||
|
||||
The deployment script needs registry credentials. These are passed as environment variables from the CI/CD workflow:
|
||||
|
||||
```bash
|
||||
# The workflow passes these variables:
|
||||
# REGISTRY_USER - from Gitea secrets
|
||||
# REGISTRY_PASS - from Gitea secrets
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Check Environment File
|
||||
|
||||
```bash
|
||||
# Verify .env file exists and has correct permissions
|
||||
ls -la /home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
# Check content (be careful not to expose secrets in logs)
|
||||
head -n 5 /home/deploy/sites/klz-cables.com/.env
|
||||
```
|
||||
|
||||
### Test Deployment Script
|
||||
|
||||
```bash
|
||||
# Run deployment script manually (requires registry credentials)
|
||||
REGISTRY_USER=your-user REGISTRY_PASS=your-pass /home/deploy/deploy.sh
|
||||
```
|
||||
|
||||
### Check Running Containers
|
||||
|
||||
```bash
|
||||
cd /home/deploy/sites/klz-cables.com
|
||||
docker-compose ps
|
||||
docker-compose logs -f app
|
||||
```
|
||||
|
||||
### Verify Environment Variables in Container
|
||||
|
||||
```bash
|
||||
# Check that environment variables are loaded
|
||||
docker-compose exec app env | grep -E "NODE_ENV|NEXT_PUBLIC|MAIL|REDIS"
|
||||
```
|
||||
|
||||
## Updating Environment Variables
|
||||
|
||||
When you need to update environment variables:
|
||||
|
||||
```bash
|
||||
# 1. SSH to the server
|
||||
ssh root@alpha.mintel.me
|
||||
|
||||
# 2. Edit the .env file
|
||||
nano /home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
# 3. Restart the containers to pick up changes
|
||||
cd /home/deploy/sites/klz-cables.com
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
|
||||
# 4. Verify the changes
|
||||
docker-compose logs -f app
|
||||
```
|
||||
|
||||
**Note**: Changes to `NEXT_PUBLIC_*` variables require rebuilding the Docker image, as they are baked into the client bundle at build time.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### .env File Not Found
|
||||
|
||||
```bash
|
||||
# Check if file exists
|
||||
ls -la /home/deploy/sites/klz-cables.com/.env
|
||||
|
||||
# If missing, create it from template
|
||||
cp .env.production /home/deploy/sites/klz-cables.com/.env
|
||||
# Then edit with actual values
|
||||
```
|
||||
|
||||
### Permission Denied
|
||||
|
||||
```bash
|
||||
# Fix ownership
|
||||
chown -R deploy:deploy /home/deploy/sites/klz-cables.com
|
||||
|
||||
# Fix .env permissions
|
||||
chmod 600 /home/deploy/sites/klz-cables.com/.env
|
||||
```
|
||||
|
||||
### Container Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker-compose logs app
|
||||
|
||||
# Check if .env is loaded
|
||||
docker-compose config
|
||||
|
||||
# Verify environment variables
|
||||
docker-compose exec app env
|
||||
```
|
||||
|
||||
### Network Issues
|
||||
|
||||
```bash
|
||||
# Verify infra network exists
|
||||
docker network ls | grep infra
|
||||
|
||||
# If missing, create it
|
||||
docker network create infra
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] `.env` file has `600` permissions (readable only by owner)
|
||||
- [ ] `.env` file is owned by `deploy:deploy`
|
||||
- [ ] `.env` file is NOT in git repository
|
||||
- [ ] All sensitive credentials are filled in
|
||||
- [ ] SSH keys are properly secured
|
||||
- [ ] Firewall rules are configured
|
||||
- [ ] HTTPS is enforced via Traefik
|
||||
- [ ] Regular backups of `.env` file are maintained
|
||||
|
||||
## Backup
|
||||
|
||||
Create a secure backup of the environment file:
|
||||
|
||||
```bash
|
||||
# Backup .env file
|
||||
cp /home/deploy/sites/klz-cables.com/.env \
|
||||
/home/deploy/backups/klz-cables.env.$(date +%Y%m%d)
|
||||
|
||||
# Set proper permissions on backup
|
||||
chmod 600 /home/deploy/backups/klz-cables.env.*
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Deployment Guide](./DEPLOYMENT.md)
|
||||
- [Docker Compose Documentation](https://docs.docker.com/compose/)
|
||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||
Reference in New Issue
Block a user