17 KiB
Environment Variables Guide
This guide explains how to use environment variables with the rxminder Kustomize deployment system.
Overview
The rxminder application supports multiple ways to configure deployments using environment variables:
- Static Configuration: Pre-generated config files
- Dynamic Configuration: Runtime environment variable injection
- Hybrid Approach: Combination of both methods
Run Profiles at a Glance
| Profile | Typical Usage | Key Files |
|---|---|---|
| Development | Vite dev server with hot reload and optional mock CouchDB | .env.local, bun run dev |
| Testing | Jest unit/integration suites against the mock database strategy | No additional env required (bun run test) |
| Production | Hardened build served by Docker/Kubernetes with real services | .env, Docker/compose manifests |
See the Run Profiles section in the project README for commands and best practices.
Environment Variable Sources
Variables are loaded in the following priority order (last wins):
~/.env- Global user environment./.env- Project-wide environment./.env.{environment}- Environment-specific (e.g.,.env.dev,.env.prod)./.env.local- Local overrides (git-ignored)- System environment variables
Quick Start
Method 1: Generate Static Configuration
# Generate configuration from your environment variables
make generate-config-dev
make generate-config-prod
# Deploy using generated config
make deploy-dev
Method 2: Dynamic Environment Injection
# Deploy with runtime environment variable substitution
make deploy-with-env-dev
make deploy-with-env-prod
Environment Files Setup
1. Global User Environment (~/.env)
Store your personal/machine-specific settings:
# ~/.env
REGISTRY_USERNAME=your_username
REGISTRY_EMAIL=your_email@example.com
KUBECONFIG=/path/to/your/kubeconfig
2. Project Environment (./.env)
Store project-wide defaults:
# ./.env
APP_NAME=rxminder
REGISTRY_URL=gitea-http.taildb3494.ts.net
IMAGE_REPOSITORY=will/rxminder
COUCHDB_DATABASE_NAME=meds_app
3. Environment-Specific Files
Development (./.env.dev)
NODE_ENV=development
LOG_LEVEL=debug
DEBUG=true
IMAGE_TAG=dev
INGRESS_HOST=rxminder-dev.local
DEV_MODE=true
ENABLE_MONITORING=false
# Development database
COUCHDB_USERNAME=admin
COUCHDB_PASSWORD=devpass123
# Development resource limits
FRONTEND_MEMORY_REQUEST=16Mi
FRONTEND_MEMORY_LIMIT=32Mi
COUCHDB_MEMORY_REQUEST=64Mi
COUCHDB_MEMORY_LIMIT=128Mi
STORAGE_SIZE=1Gi
Production (./.env.prod)
NODE_ENV=production
LOG_LEVEL=warn
DEBUG=false
IMAGE_TAG=v1.0.0
INGRESS_HOST=rxminder.yourdomain.com
ENABLE_MONITORING=true
ENABLE_METRICS=true
ENABLE_TRACING=true
# Production performance
CACHE_TTL=3600
REQUEST_TIMEOUT=30000
MAX_CONNECTIONS=200
# Production resources
FRONTEND_REPLICAS=3
FRONTEND_MEMORY_REQUEST=256Mi
FRONTEND_MEMORY_LIMIT=512Mi
COUCHDB_MEMORY_REQUEST=512Mi
COUCHDB_MEMORY_LIMIT=1Gi
STORAGE_SIZE=10Gi
STORAGE_CLASS=ssd
# Security
ENABLE_SECURITY_HEADERS=true
ENABLE_RATE_LIMITING=true
CORS_ORIGIN=https://rxminder.yourdomain.com
# TLS/SSL
ENABLE_TLS=true
CERT_MANAGER_ISSUER=letsencrypt-prod
Staging (./.env.staging)
NODE_ENV=staging
LOG_LEVEL=info
DEBUG=false
IMAGE_TAG=staging
INGRESS_HOST=staging.rxminder.yourdomain.com
ENABLE_MONITORING=true
# Staging resources (between dev and prod)
FRONTEND_REPLICAS=2
FRONTEND_MEMORY_REQUEST=128Mi
FRONTEND_MEMORY_LIMIT=256Mi
STORAGE_SIZE=5Gi
4. Local Overrides (./.env.local)
Note: This file should be in .gitignore and used for sensitive local settings:
# ./.env.local (DO NOT COMMIT)
COUCHDB_PASSWORD=your_secure_password
REGISTRY_PASSWORD=your_registry_token
API_SECRET_KEY=your_secret_key
JWT_SECRET=your_jwt_secret
# Local development overrides
INGRESS_HOST=localhost:8080
DEV_API_URL=http://localhost:5984
Available Environment Variables
Core Application Variables
| Variable | Default | Description |
|---|---|---|
APP_NAME |
rxminder |
Application name used in labels and resources |
NODE_ENV |
production |
Runtime environment (development, staging, production) |
LOG_LEVEL |
info |
Logging level (debug, info, warn, error) |
DEBUG |
false |
Enable debug mode |
APP_VERSION |
1.0.0 |
Application version |
Container Registry Variables
| Variable | Default | Description |
|---|---|---|
REGISTRY_URL |
gitea-http.taildb3494.ts.net |
Container registry URL |
IMAGE_REPOSITORY |
will/rxminder |
Image repository path |
IMAGE_TAG |
latest |
Image tag to deploy |
REGISTRY_USERNAME |
- | Registry authentication username |
REGISTRY_PASSWORD |
- | Registry authentication password |
REGISTRY_EMAIL |
- | Registry authentication email |
Database Variables
| Variable | Default | Description |
|---|---|---|
DB_HOST |
rxminder-couchdb-service |
Database host |
DB_PORT |
5984 |
Database port |
COUCHDB_DATABASE_NAME |
meds_app |
CouchDB database name |
COUCHDB_USERNAME |
admin |
Database username |
COUCHDB_PASSWORD |
- | Database password (required) |
Email (Mailgun) Variables
| Variable | Default | Description |
|---|---|---|
VITE_MAILGUN_API_KEY |
required | Mailgun API key used for authenticated requests |
VITE_MAILGUN_DOMAIN |
required | Mailgun sending domain (e.g. mg.yourdomain.com) |
VITE_MAILGUN_BASE_URL |
https://api.mailgun.net/v3 |
Mailgun REST API base URL |
VITE_MAILGUN_FROM_NAME |
Medication Reminder |
Friendly name used in the from header |
VITE_MAILGUN_FROM_EMAIL |
required | Email address used in the from header (must belong to the configured domain) |
Tip: When any required Mailgun variables are missing, the application falls back to a development mode that logs email previews instead of sending real messages. Configure the variables above in
.env.local(git ignored) before testing real email flows.
Network & Ingress Variables
| Variable | Default | Description |
|---|---|---|
INGRESS_HOST |
Environment-specific | Ingress hostname |
INGRESS_CLASS |
nginx |
Ingress class to use |
ENABLE_TLS |
false |
Enable TLS/HTTPS |
CERT_MANAGER_ISSUER |
letsencrypt-prod |
Certificate issuer |
CORS_ORIGIN |
* |
CORS allowed origins |
When running via
docker compose up --build, CouchDB CORS settings are sourced fromcouchdb-config/cors.ini. Update theoriginslist in that file to add additional frontend domains.
Performance Variables
| Variable | Default | Description |
|---|---|---|
FRONTEND_REPLICAS |
1 |
Number of frontend replicas |
CACHE_TTL |
1800 |
Cache time-to-live in seconds |
REQUEST_TIMEOUT |
30000 |
Request timeout in milliseconds |
MAX_CONNECTIONS |
100 |
Maximum concurrent connections |
Resource Limits
| Variable | Default | Description |
|---|---|---|
FRONTEND_MEMORY_REQUEST |
32Mi |
Frontend memory request |
FRONTEND_MEMORY_LIMIT |
64Mi |
Frontend memory limit |
FRONTEND_CPU_REQUEST |
20m |
Frontend CPU request |
FRONTEND_CPU_LIMIT |
40m |
Frontend CPU limit |
COUCHDB_MEMORY_REQUEST |
64Mi |
CouchDB memory request |
COUCHDB_MEMORY_LIMIT |
128Mi |
CouchDB memory limit |
STORAGE_SIZE |
1Gi |
Storage volume size |
STORAGE_CLASS |
standard |
Storage class |
Monitoring & Observability
| Variable | Default | Description |
|---|---|---|
ENABLE_MONITORING |
false |
Enable monitoring features |
ENABLE_METRICS |
false |
Enable metrics collection |
ENABLE_TRACING |
false |
Enable distributed tracing |
METRICS_PORT |
9090 |
Metrics server port |
Security Variables
| Variable | Default | Description |
|---|---|---|
ENABLE_SECURITY_HEADERS |
false |
Enable security headers |
ENABLE_RATE_LIMITING |
false |
Enable rate limiting |
API_SECRET_KEY |
- | API secret key |
JWT_SECRET |
- | JWT signing secret |
Bootstrap Admin Variables
These variables control the default admin account created/updated at app startup by the frontend seeder. They are read at build-time (Vite), so changing them requires rebuilding the frontend image.
| Variable | Default | Description |
|---|---|---|
VITE_ADMIN_EMAIL |
admin@localhost |
Email of the default admin user |
VITE_ADMIN_PASSWORD |
admin123! |
Password for the default admin user |
Notes:
- To change these in Docker, set build args in
docker-compose.yamlor define them in.envand rebuild:docker compose build frontend && docker compose up -d. - The seeder is idempotent: if a user with this email exists, it updates role/status and keeps the latest password you set.
Usage Examples
Basic Development Setup
- Create your development environment file:
cat > .env.dev << EOF
NODE_ENV=development
LOG_LEVEL=debug
DEBUG=true
IMAGE_TAG=dev
INGRESS_HOST=rxminder-dev.local
COUCHDB_PASSWORD=devpass123
EOF
- Generate configuration and deploy:
make generate-config-dev
make deploy-dev
Production Deployment with Secrets
- Set up your production environment:
# .env.prod (commit this)
NODE_ENV=production
LOG_LEVEL=warn
IMAGE_TAG=v1.0.0
INGRESS_HOST=rxminder.yourdomain.com
ENABLE_TLS=true
# .env.local (DO NOT commit)
COUCHDB_PASSWORD=your_secure_production_password
REGISTRY_PASSWORD=your_registry_token
- Deploy with environment variables:
make deploy-with-env-prod
Dynamic Configuration Updates
Update configuration without rebuilding:
# Update environment variable
export LOG_LEVEL=debug
# Deploy with updated configuration
./scripts/deploy-with-env.sh prod apply
Testing Different Configurations
# Test with different image tag
export IMAGE_TAG=feature-branch
make diff-with-env-dev
# Test with different resources
export FRONTEND_REPLICAS=5
./scripts/deploy-with-env.sh prod dry-run
Available Commands
Configuration Generation
make generate-config-dev # Generate dev config from env vars
make generate-config-prod # Generate prod config from env vars
make generate-config-staging # Generate staging config from env vars
make generate-config-all # Generate all environment configs
make validate-config # Validate generated configuration
make generate-secrets-template # Generate secrets template files
Environment-Aware Deployment
make deploy-with-env-dev # Deploy dev with env vars
make deploy-with-env-prod # Deploy prod with env vars
make deploy-with-env-staging # Deploy staging with env vars
make undeploy-with-env-dev # Remove dev deployment
make diff-with-env-dev # Show dev diff with env vars
make status-with-env-dev # Show dev status with env vars
Direct Script Usage
# Generate configuration
./scripts/generate-config.sh dev
./scripts/generate-config.sh prod --secrets
# Deploy with environment variables
./scripts/deploy-with-env.sh dev apply
./scripts/deploy-with-env.sh prod diff
./scripts/deploy-with-env.sh staging delete
Best Practices
Security
-
Never commit sensitive data:
- Add
.env.localto.gitignore - Use external secret management for production
- Rotate passwords regularly
- Add
-
Use environment-specific files:
.env.devfor development settings.env.prodfor production configuration.env.localfor sensitive overrides
Organization
-
Group related variables:
- Database settings together
- Resource limits together
- Feature flags together
-
Use descriptive names:
FRONTEND_MEMORY_LIMITvsMEM_LIMITENABLE_DEBUG_MODEvsDEBUG
-
Document your variables:
- Add comments explaining purpose
- Include example values
- Note required vs optional
Development Workflow
-
Start with examples:
cp .env.example .env.dev # Edit .env.dev with your settings -
Test configurations:
make generate-config-dev make kustomize-dry-run-dev -
Validate before deploying:
make validate-config make diff-with-env-dev
Troubleshooting
Common Issues
-
Variables not being loaded:
- Check file permissions (
chmod 644 .env.*) - Verify file format (no spaces around
=) - Check file encoding (UTF-8)
- Check file permissions (
-
Configuration not updating:
- Regenerate config:
make generate-config-dev - Clear cached resources:
kubectl delete configmap -l app=rxminder
- Regenerate config:
-
Deployment failures:
- Validate syntax:
make validate-config - Check logs:
kubectl logs -l app=rxminder - Verify resources:
kubectl describe deployment rxminder-frontend
- Validate syntax:
Debug Commands
# Check loaded environment variables
./scripts/generate-config.sh dev --dry-run
# Validate generated configuration
make validate-config
# Show what would be deployed
./scripts/deploy-with-env.sh dev dry-run
# Check current deployment status
make status-with-env-dev
Migration from Legacy System
Step 1: Extract Current Configuration
# Export current settings to environment file
echo "APP_NAME=rxminder" > .env.dev
echo "IMAGE_TAG=dev" >> .env.dev
# ... add other variables
Step 2: Test New System
# Generate and validate configuration
make generate-config-dev
make validate-config
# Test deployment
make kustomize-dry-run-dev
Step 3: Deploy
# Deploy using new system
make deploy-with-env-dev
# Verify deployment
make status-with-env-dev
External Secret Management
For production environments, integrate with external secret management:
HashiCorp Vault Integration
# .env.prod
VAULT_ADDR=https://vault.yourdomain.com
VAULT_ROLE=rxminder-prod
VAULT_SECRET_PATH=secret/rxminder/prod
# Use Vault Agent or CSI driver to inject secrets
Kubernetes External Secrets
# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: rxminder-secrets
spec:
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: couchdb-secret
data:
- secretKey: password
remoteRef:
key: rxminder/prod
property: couchdb_password
AWS Secrets Manager
# .env.prod
AWS_REGION=us-west-2
AWS_SECRET_NAME=rxminder/prod/couchdb
# Use AWS Load Balancer Controller or external-secrets-operator
Summary
The environment variable system provides:
- Flexibility: Configure deployments without changing code
- Security: Keep sensitive data out of version control
- Consistency: Standardized configuration across environments
- Maintainability: Clear separation of configuration and code
Choose the approach that best fits your workflow:
- Static generation for stable, version-controlled configurations
- Dynamic injection for flexible, runtime configurations
- Hybrid approach for the best of both worlds
For more information, see: