Files
klz-cables.com/docs/DEPLOYMENT.md
2026-01-29 19:47:55 +01:00

10 KiB

KLZ Cables - Deployment Guide

This document explains the deployment process and environment variable management for the KLZ Cables application.

Table of Contents

Overview

The application uses a clean, robust, fully automated environment variable strategy:

  • 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

Architecture

┌─────────────────────────────────────────────────────────────┐
│ 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              │
└─────────────────────────────────────────────────────────────┘

Environment Variables

Build-Time Variables (NEXT_PUBLIC_*)

These are embedded into the JavaScript bundle during build and are visible to the client:

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:)
STRAPI_DATABASE_NAME Yes Strapi database name
STRAPI_DATABASE_USERNAME Yes Strapi database username
STRAPI_DATABASE_PASSWORD Yes Strapi database password
STRAPI_URL Yes URL of the Strapi CMS
APP_KEYS Yes Strapi application keys (comma-separated)
API_TOKEN_SALT Yes Strapi API token salt
ADMIN_JWT_SECRET Yes Strapi admin JWT secret
TRANSFER_TOKEN_SALT Yes Strapi transfer token salt
JWT_SECRET Yes Strapi JWT secret

Local Development

Setup

  1. Copy the example environment file:

    cp .env.example .env
    
  2. Edit .env and fill in your local configuration:

    NODE_ENV=development
    NEXT_PUBLIC_BASE_URL=http://localhost:3000
    # Add other variables as needed
    
  3. Install dependencies:

    npm install
    
  4. Run the development server:

    npm run dev
    

Testing Docker Build Locally

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

# 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 pull registry.infra.mintel.me/mintel/klz-cables.com:latest

# Restart the services
docker-compose down
docker-compose up -d

# 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 as a template.

Troubleshooting

Build Failures

Problem: Build fails with "Environment validation failed"

Solution: Ensure all required NEXT_PUBLIC_* variables are provided as build arguments:

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 .

Runtime Failures

Problem: Container starts but application crashes

Solution: Check that the .env file exists and contains all required runtime variables:

# On the server
cat /home/deploy/sites/klz-cables.com/.env

# Check container logs
docker-compose logs app

Missing Environment Variables

Problem: Features not working (email, analytics, etc.)

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:

    git commit --allow-empty -m "Trigger redeploy"
    git push origin main
    
  4. Check the variable is loaded on the server:

    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:

docker network ls | grep infra
docker network inspect infra

Security Best Practices

  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

Additional Resources