14 KiB
Environment Variables Reference
Complete guide to environment variables used in Flyer Crawler.
Quick Reference
Minimum Required Variables (Development)
| Variable | Example | Purpose |
|---|---|---|
DB_HOST |
localhost |
PostgreSQL host |
DB_USER |
postgres |
PostgreSQL username |
DB_PASSWORD |
postgres |
PostgreSQL password |
DB_NAME |
flyer_crawler_dev |
Database name |
REDIS_URL |
redis://localhost:6379 |
Redis connection URL |
JWT_SECRET |
(32+ character string) | JWT signing key |
GEMINI_API_KEY |
AIzaSy... |
Google Gemini API |
Source of Truth
The Zod schema at src/config/env.ts is the authoritative source for all environment variables. If a variable is not in this file, it is not used by the application.
Configuration by Environment
Production
| Aspect | Details |
|---|---|
| Location | Gitea CI/CD secrets injected at deployment |
| Path | /var/www/flyer-crawler.projectium.com/ |
| File | No .env file - all from CI/CD secrets |
Test
| Aspect | Details |
|---|---|
| Location | Gitea CI/CD secrets + .env.test overrides |
| Path | /var/www/flyer-crawler-test.projectium.com/ |
| File | .env.test for test-specific values |
Development Container
| Aspect | Details |
|---|---|
| Location | .env.local file in project root |
| Priority | Overrides defaults in compose.dev.yml |
| File | .env.local (gitignored) |
Complete Variable Reference
Database Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
DB_HOST |
Yes | - | PostgreSQL host |
DB_PORT |
No | 5432 |
PostgreSQL port |
DB_USER |
Yes | - | Database username |
DB_PASSWORD |
Yes | - | Database password |
DB_NAME |
Yes | - | Database name |
Environment-Specific Variables (Gitea Secrets):
| Variable | Environment | Description |
|---|---|---|
DB_USER_PROD |
Production | Production database user |
DB_PASSWORD_PROD |
Production | Production database pass |
DB_DATABASE_PROD |
Production | Production database name |
DB_USER_TEST |
Test | Test database user |
DB_PASSWORD_TEST |
Test | Test database password |
DB_DATABASE_TEST |
Test | Test database name |
Redis Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
REDIS_URL |
Yes | - | Redis connection URL |
REDIS_PASSWORD |
No | - | Redis password (optional) |
URL Format: redis://[user:password@]host:port
Examples:
# Development (no auth)
REDIS_URL=redis://localhost:6379
# Production (with auth)
REDIS_URL=redis://:${REDIS_PASSWORD_PROD}@localhost:6379
Authentication
| Variable | Required | Min Length | Description |
|---|---|---|---|
JWT_SECRET |
Yes | 32 chars | JWT token signing key |
JWT_SECRET_PREVIOUS |
No | - | Previous key (rotation) |
GOOGLE_CLIENT_ID |
No | - | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
No | - | Google OAuth secret |
GITHUB_CLIENT_ID |
No | - | GitHub OAuth client ID |
GITHUB_CLIENT_SECRET |
No | - | GitHub OAuth secret |
Generate Secure Secret:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
AI Services
| Variable | Required | Description |
|---|---|---|
GEMINI_API_KEY |
Yes* | Google Gemini API key |
GEMINI_RPM |
No | Rate limit (default: 5) |
AI_PRICE_QUALITY_THRESHOLD |
No | Quality threshold (default: 0.5) |
*Required for flyer processing. Application works without it but cannot extract flyer data.
Get API Key: Google AI Studio
Google Services
| Variable | Required | Description |
|---|---|---|
GOOGLE_MAPS_API_KEY |
No | Google Maps Geocoding API |
GOOGLE_CLIENT_ID |
No | OAuth (see Authentication above) |
GOOGLE_CLIENT_SECRET |
No | OAuth (see Authentication above) |
UPC Lookup APIs
| Variable | Required | Description |
|---|---|---|
UPC_ITEM_DB_API_KEY |
No | UPC Item DB API key |
BARCODE_LOOKUP_API_KEY |
No | Barcode Lookup API key |
Application Settings
| Variable | Required | Default | Description |
|---|---|---|---|
NODE_ENV |
No | development |
Environment mode |
PORT |
No | 3001 |
Backend server port |
FRONTEND_URL |
No | - | Frontend URL (CORS) |
BASE_URL |
No | - | API base URL |
STORAGE_PATH |
No | (see below) | Flyer image storage path |
NODE_ENV Values: development, test, staging, production
Default STORAGE_PATH: /var/www/flyer-crawler.projectium.com/flyer-images
Email/SMTP Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
SMTP_HOST |
No | - | SMTP server hostname |
SMTP_PORT |
No | 587 |
SMTP server port |
SMTP_USER |
No | - | SMTP username |
SMTP_PASS |
No | - | SMTP password |
SMTP_SECURE |
No | false |
Use TLS |
SMTP_FROM_EMAIL |
No | - | From address for emails |
Note: Email functionality degrades gracefully if not configured.
Worker Configuration
| Variable | Default | Description |
|---|---|---|
WORKER_CONCURRENCY |
1 |
Main worker concurrency |
WORKER_LOCK_DURATION |
30000 |
Lock duration (ms) |
EMAIL_WORKER_CONCURRENCY |
10 |
Email worker concurrency |
ANALYTICS_WORKER_CONCURRENCY |
1 |
Analytics worker concurrency |
CLEANUP_WORKER_CONCURRENCY |
10 |
Cleanup worker concurrency |
WEEKLY_ANALYTICS_WORKER_CONCURRENCY |
1 |
Weekly analytics concurrency |
Error Tracking (Bugsink/Sentry)
| Variable | Required | Default | Description |
|---|---|---|---|
SENTRY_DSN |
No | - | Backend Sentry DSN |
SENTRY_ENABLED |
No | true |
Enable error tracking |
SENTRY_ENVIRONMENT |
No | NODE_ENV | Environment name for errors |
SENTRY_DEBUG |
No | false |
Enable Sentry SDK debug logging |
VITE_SENTRY_DSN |
No | - | Frontend Sentry DSN |
VITE_SENTRY_ENABLED |
No | true |
Enable frontend error tracking |
VITE_SENTRY_DEBUG |
No | false |
Frontend SDK debug logging |
DSN Format: http://[key]@[host]:[port]/[project_id]
Dev Container DSNs:
# Backend (internal)
SENTRY_DSN=http://<key>@localhost:8000/1
# Frontend (via nginx proxy)
VITE_SENTRY_DSN=https://<key>@localhost/bugsink-api/2
Configuration Files
| File | Purpose |
|---|---|
src/config/env.ts |
Zod schema validation - source of truth |
ecosystem.config.cjs |
PM2 process manager (production) |
ecosystem.dev.config.cjs |
PM2 process manager (development) |
.gitea/workflows/deploy-to-prod.yml |
Production deployment workflow |
.gitea/workflows/deploy-to-test.yml |
Test deployment workflow |
.env.example |
Template with all variables |
.env.local |
Dev container overrides (not in git) |
.env.test |
Test environment overrides (not in git) |
Adding New Variables
Checklist
- Update Zod Schema - Edit
src/config/env.ts - Add to Gitea Secrets - For prod/test environments
- Update Deployment Workflows -
.gitea/workflows/*.yml - Update PM2 Config -
ecosystem.config.cjs - Update .env.example - Template for developers
- Update this document - Add to appropriate section
Step-by-Step
1. Update Zod Schema
Edit src/config/env.ts:
const envSchema = z.object({
// ... existing variables ...
newSection: z.object({
newVariable: z.string().min(1, 'NEW_VARIABLE is required'),
}),
});
// In loadEnvVars():
newSection: {
newVariable: process.env.NEW_VARIABLE,
},
2. Add to Gitea Secrets
- Go to Gitea repository Settings > Secrets
- Add
NEW_VARIABLEwith production value - Add
NEW_VARIABLE_TESTif test needs different value
3. Update Deployment Workflows
Edit .gitea/workflows/deploy-to-prod.yml:
env:
NEW_VARIABLE: ${{ secrets.NEW_VARIABLE }}
Edit .gitea/workflows/deploy-to-test.yml:
env:
NEW_VARIABLE: ${{ secrets.NEW_VARIABLE_TEST }}
4. Update PM2 Config
Edit ecosystem.config.cjs:
module.exports = {
apps: [
{
env: {
NEW_VARIABLE: process.env.NEW_VARIABLE,
},
},
],
};
Security Best Practices
Do
- Use Gitea Secrets for prod/test
- Use
.env.localfor dev (gitignored) - Generate secrets with cryptographic randomness
- Rotate secrets regularly
- Use environment-specific database users
Do Not
- Commit secrets to git
- Use short or predictable secrets
- Share secrets across environments
- Log sensitive values
Secret Generation
# Generate secure random secrets (64 hex characters)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Example output:
# a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Database Users by Environment
| Environment | User | Database |
|---|---|---|
| Production | flyer_crawler_prod |
flyer-crawler-prod |
| Test | flyer_crawler_test |
flyer-crawler-test |
| Development | postgres |
flyer_crawler_dev |
Validation
Environment variables are validated at startup via src/config/env.ts.
Startup Validation
If validation fails, you will see:
╔════════════════════════════════════════════════════════════════╗
║ CONFIGURATION ERROR - APPLICATION STARTUP ║
╚════════════════════════════════════════════════════════════════╝
The following environment variables are missing or invalid:
- database.host: DB_HOST is required
- auth.jwtSecret: JWT_SECRET must be at least 32 characters
Please check your .env file or environment configuration.
Debugging Configuration
# Check what variables are set (dev container)
podman exec flyer-crawler-dev env | grep -E "^(DB_|REDIS_|JWT_|SENTRY_)"
# Test database connection
podman exec flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev -c "SELECT 1;"
# Test Redis connection
podman exec flyer-crawler-redis redis-cli ping
Troubleshooting
Variable Not Found
Error: Missing required environment variable: JWT_SECRET
Solutions:
- Check
.env.localexists and has the variable - Verify variable name matches schema exactly
- Restart the application after changes
Invalid Value
Error: JWT_SECRET must be at least 32 characters
Solution: Generate a longer secret value.
Wrong Environment Selected
Check NODE_ENV is set correctly:
| Value | Purpose |
|---|---|
development |
Local dev container |
test |
CI/CD test server |
staging |
Pre-production testing |
production |
Production server |
Database Connection Issues
# Development
podman exec flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev -c "SELECT 1;"
# If connection fails, check:
# 1. Container is running: podman ps
# 2. DB_HOST matches container network
# 3. DB_PASSWORD is correct
Related Documentation
- QUICKSTART.md - Quick setup guide
- INSTALL.md - Detailed installation
- DEV-CONTAINER.md - Dev container setup
- DEPLOYMENT.md - Production deployment
- AUTHENTICATION.md - OAuth setup
- ADR-007 - Configuration decisions
Last updated: January 2026