11 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
.envfile at runtime - Fully automated:
.envfile is automatically generated from Gitea secrets on every deployment - No manual steps: No need to manually create or update
.envfiles 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 (NEXTPUBLIC*)
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) |
UMAMI_WEBSITE_ID |
❌ No | Umami analytics website ID (passed as prop) |
UMAMI_API_ENDPOINT |
❌ No | Backend-only Umami analytics API target (internal) |
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
-
Copy the example environment file:
cp .env.example .env -
Edit
.envand fill in your local configuration:NODE_ENV=development NEXT_PUBLIC_BASE_URL=http://localhost:3000 # Add other variables as needed -
Install dependencies:
npm install -
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 UMAMI_WEBSITE_ID=your-id \
--build-arg UMAMI_API_ENDPOINT=https://analytics.infra.mintel.me \
-t klz-cables:local .
# Run with runtime environment file
docker run --env-file .env -p 3000:3000 klz-cables:local
Production Deployment
Prerequisites
-
Server Setup: Ensure the production server has:
- Docker and Docker Compose installed
- Traefik reverse proxy running
- Network
infracreated - Project directory exists:
/home/deploy/sites/klz-cables.com
-
Gitea Secrets: Configure the following secrets in your Gitea repository:
Registry Access:
REGISTRY_USER- Docker registry usernameREGISTRY_PASS- Docker registry password
Build-Time Variables:
NEXT_PUBLIC_BASE_URL- Production URL (e.g.,https://klz-cables.com)UMAMI_WEBSITE_ID- Umami analytics IDUMAMI_API_ENDPOINT- Umami API endpoint
Runtime Variables:
SENTRY_DSN- Error tracking DSNMAIL_HOST- SMTP serverMAIL_PORT- SMTP portMAIL_USERNAME- SMTP usernameMAIL_PASSWORD- SMTP passwordMAIL_FROM- Sender emailMAIL_RECIPIENTS- Recipient emails (comma-separated)REDIS_URL- Redis connection URLREDIS_KEY_PREFIX- Redis key prefix
Infrastructure:
REGISTRY_USER- Docker registry usernameREGISTRY_PASS- Docker registry passwordALPHA_SSH_KEY- SSH private key for deployment server
Notifications:
GOTIFY_URL- Gotify notification server URLGOTIFY_TOKEN- Gotify application token
Deployment Process
The deployment is fully automated via Gitea Actions:
- Trigger: Push to
mainbranch - Build: Docker image is built with
NEXT_PUBLIC_*build arguments - Push: Image is pushed to private registry
- Generate .env: Workflow creates
.envfile from all Gitea secrets - Upload .env: File is uploaded to server via SCP and secured (600 permissions)
- Deploy: SSH to server, pull image, and run docker-compose
- 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 UMAMI_WEBSITE_ID=your-id \
--build-arg UMAMI_API_ENDPOINT=https://analytics.infra.mintel.me \
-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:
-
Check that the secret is configured in Gitea
-
Verify the workflow includes it in the
.envgeneration (see.gitea/workflows/deploy.yml) -
Redeploy to regenerate the
.envfile:git commit --allow-empty -m "Trigger redeploy" git push origin main -
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:
- Check the workflow logs for errors in the "📝 Preparing environment configuration" step
- Manually trigger a deployment by pushing to main
- 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
- Never commit
.envfiles with real credentials to git - Use Gitea/GitHub secrets for CI/CD workflows
- Restrict SSH key access to deployment server
- Rotate credentials regularly (SMTP passwords, API keys, etc.)
- Use HTTPS for all production URLs
- Monitor logs for suspicious activity