# KLZ Cables - Deployment Guide This document explains the deployment process and environment variable management for the KLZ Cables application. ## Table of Contents - [Overview](#overview) - [Environment Variables](#environment-variables) - [Local Development](#local-development) - [Production Deployment](#production-deployment) - [Troubleshooting](#troubleshooting) ## 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: ```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 # 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 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`](.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: ```bash 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: ```bash # 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: ```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 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 - [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/)