12 KiB
Environment Variables
Complete documentation for all environment variables used in The Biergarten App.
Overview
The application uses environment variables for configuration across:
- .NET API Backend - Database connections, JWT secrets
- Next.js Frontend - External services, authentication
- Docker Containers - Runtime configuration
Configuration Patterns
Backend (.NET API)
Direct environment variable access via Environment.GetEnvironmentVariable().
Frontend (Next.js)
Centralized configuration module at src/Website/src/config/env/index.ts with Zod validation.
Docker
Environment-specific .env files loaded via env_file: in docker-compose.yaml:
.env.dev- Development.env.test- Testing.env.prod- Production
Backend Variables (.NET API)
Database Connection
Option 1: Component-Based (Recommended for Docker)
Build connection string from individual components:
DB_SERVER=sqlserver,1433 # SQL Server host and port
DB_NAME=Biergarten # Database name
DB_USER=sa # SQL Server username
DB_PASSWORD=YourStrong!Passw0rd # SQL Server password
DB_TRUST_SERVER_CERTIFICATE=True # Optional, defaults to True
Option 2: Full Connection String (Local Development)
Provide complete connection string:
DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
Priority: DB_CONNECTION_STRING is checked first. If not found, connection string is built from components.
Implementation: See DefaultSqlConnectionFactory.cs
JWT Authentication
JWT_SECRET=your-secret-key-minimum-32-characters-required
- Required: Yes
- Minimum Length: 32 characters (enforced)
- Purpose: Signs JWT tokens for user authentication
- Algorithm: HS256 (HMAC-SHA256)
Generate Secret:
# macOS/Linux
openssl rand -base64 127
# Windows PowerShell
[Convert]::ToBase64String((1..127 | %{Get-Random -Max 256}))
Additional JWT Settings (appsettings.json):
{
"Jwt": {
"ExpirationMinutes": 60,
"Issuer": "biergarten-api",
"Audience": "biergarten-users"
}
}
Migration Control
CLEAR_DATABASE=true
- Required: No
- Default: false
- Effect: If "true", drops and recreates database during migrations
- Usage: Development and testing environments ONLY
- Warning: NEVER use in production
ASP.NET Core Configuration
ASPNETCORE_ENVIRONMENT=Development # Development, Production, Staging
ASPNETCORE_URLS=http://0.0.0.0:8080 # Binding address and port
DOTNET_RUNNING_IN_CONTAINER=true # Flag for container execution
Frontend Variables (Next.js)
Create .env.local in the Website/ directory.
Base Configuration
BASE_URL=http://localhost:3000 # Application base URL
NODE_ENV=development # Environment: development, production, test
Authentication & Sessions
# Token signing secrets (use openssl rand -base64 127)
CONFIRMATION_TOKEN_SECRET=<generated-secret> # Email confirmation tokens
RESET_PASSWORD_TOKEN_SECRET=<generated-secret> # Password reset tokens
SESSION_SECRET=<generated-secret> # Session cookie signing
# Session configuration
SESSION_TOKEN_NAME=biergarten # Cookie name (optional)
SESSION_MAX_AGE=604800 # Cookie max age in seconds (optional, default: 1 week)
Security Requirements:
- All secrets should be 127+ characters
- Generate using cryptographically secure random functions
- Never reuse secrets across environments
- Rotate secrets periodically in production
Database (Current - Prisma/Postgres)
Note: Frontend currently uses Neon Postgres. Will migrate to .NET API.
POSTGRES_PRISMA_URL=postgresql://user:pass@host/db?pgbouncer=true # Pooled connection
POSTGRES_URL_NON_POOLING=postgresql://user:pass@host/db # Direct connection (migrations)
SHADOW_DATABASE_URL=postgresql://user:pass@host/shadow_db # Prisma shadow DB (optional)
External Services
Cloudinary (Image Hosting)
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your-cloud-name # Public, client-accessible
CLOUDINARY_KEY=your-api-key # Server-side API key
CLOUDINARY_SECRET=your-api-secret # Server-side secret
Setup Steps:
- Sign up at cloudinary.com
- Navigate to Dashboard
- Copy Cloud Name, API Key, and API Secret
Note: NEXT_PUBLIC_ prefix makes variable accessible in client-side code.
Mapbox (Maps & Geocoding)
MAPBOX_ACCESS_TOKEN=pk.your-public-token
Setup Steps:
- Create account at mapbox.com
- Navigate to Account → Tokens
- Create new token with public scopes
- Copy access token
SparkPost (Email Service)
SPARKPOST_API_KEY=your-api-key
SPARKPOST_SENDER_ADDRESS=noreply@yourdomain.com
Setup Steps:
- Sign up at sparkpost.com
- Verify sending domain or use sandbox
- Create API key with "Send via SMTP" permission
- Configure sender address (must match verified domain)
Admin Account (Seeding)
ADMIN_PASSWORD=SecureAdminPassword123! # Initial admin password for seeding
- Required: No (only needed for seeding)
- Purpose: Sets admin account password during database seeding
- Security: Use strong password, change immediately in production
Docker-Specific Variables
SQL Server Container
SA_PASSWORD=YourStrong!Passw0rd # SQL Server SA password
ACCEPT_EULA=Y # Accept SQL Server EULA (required)
MSSQL_PID=Express # SQL Server edition (Express, Developer, Enterprise)
Password Requirements:
- Minimum 8 characters
- Uppercase, lowercase, digits, and special characters
- Maps to
DB_PASSWORDfor application containers
Environment File Structure
Backend/Docker (Root Directory)
.env.example # Template (tracked in Git)
.env.dev # Development config (gitignored)
.env.test # Testing config (gitignored)
.env.prod # Production config (gitignored)
Setup:
cp .env.example .env.dev
# Edit .env.dev with your values
Docker Compose Mapping:
docker-compose.dev.yaml→.env.devdocker-compose.test.yaml→.env.testdocker-compose.prod.yaml→.env.prod
Frontend (Website Directory)
.env.local # Local development (gitignored)
.env.production # Production (gitignored)
Setup:
cd Website
touch .env.local
# Add frontend variables
Variable Reference Table
| Variable | Backend | Frontend | Docker | Required | Notes |
|---|---|---|---|---|---|
| Database | |||||
DB_SERVER |
✓ | ✓ | Yes* | SQL Server address | |
DB_NAME |
✓ | ✓ | Yes* | Database name | |
DB_USER |
✓ | ✓ | Yes* | SQL username | |
DB_PASSWORD |
✓ | ✓ | Yes* | SQL password | |
DB_CONNECTION_STRING |
✓ | Yes* | Alternative to components | ||
DB_TRUST_SERVER_CERTIFICATE |
✓ | ✓ | No | Defaults to True | |
SA_PASSWORD |
✓ | Yes | SQL Server container | ||
| Authentication (Backend) | |||||
JWT_SECRET |
✓ | ✓ | Yes | Min 32 chars | |
| Authentication (Frontend) | |||||
CONFIRMATION_TOKEN_SECRET |
✓ | Yes | Email confirmation | ||
RESET_PASSWORD_TOKEN_SECRET |
✓ | Yes | Password reset | ||
SESSION_SECRET |
✓ | Yes | Session signing | ||
SESSION_TOKEN_NAME |
✓ | No | Default: "biergarten" | ||
SESSION_MAX_AGE |
✓ | No | Default: 604800 | ||
| Base Configuration | |||||
BASE_URL |
✓ | Yes | App base URL | ||
NODE_ENV |
✓ | Yes | Node environment | ||
ASPNETCORE_ENVIRONMENT |
✓ | ✓ | Yes | ASP.NET environment | |
ASPNETCORE_URLS |
✓ | ✓ | Yes | API binding address | |
| Database (Frontend - Current) | |||||
POSTGRES_PRISMA_URL |
✓ | Yes | Pooled connection | ||
POSTGRES_URL_NON_POOLING |
✓ | Yes | Direct connection | ||
SHADOW_DATABASE_URL |
✓ | No | Prisma shadow DB | ||
| External Services | |||||
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME |
✓ | Yes | Public, client-side | ||
CLOUDINARY_KEY |
✓ | Yes | Server-side | ||
CLOUDINARY_SECRET |
✓ | Yes | Server-side | ||
MAPBOX_ACCESS_TOKEN |
✓ | Yes | Maps/geocoding | ||
SPARKPOST_API_KEY |
✓ | Yes | Email service | ||
SPARKPOST_SENDER_ADDRESS |
✓ | Yes | From address | ||
| Other | |||||
ADMIN_PASSWORD |
✓ | No | Seeding only | ||
CLEAR_DATABASE |
✓ | ✓ | No | Dev/test only | |
ACCEPT_EULA |
✓ | Yes | SQL Server EULA | ||
MSSQL_PID |
✓ | No | SQL Server edition | ||
DOTNET_RUNNING_IN_CONTAINER |
✓ | ✓ | No | Container flag |
* Either DB_CONNECTION_STRING OR the component variables (DB_SERVER, DB_NAME, DB_USER, DB_PASSWORD) must be provided.
Validation
Backend Validation
Variables are validated at startup:
- Missing required variables cause application to fail
- JWT_SECRET length is enforced (min 32 chars)
- Connection string format is validated
Frontend Validation
Zod schemas validate variables at runtime:
- Type checking (string, number, URL, etc.)
- Format validation (email, URL patterns)
- Required vs optional enforcement
Location: src/Website/src/config/env/index.ts
Security Best Practices
- Never commit
.envfiles - Add to.gitignore - Use
.env.exampleas template - Track in Git without sensitive values - Generate strong secrets - Use cryptographically secure random generators
- Rotate secrets regularly - Especially after team member changes
- Use different secrets per environment - Production ≠ Development
- Restrict access - Limit who can view production secrets
- Use secret management - Consider HashiCorp Vault, AWS Secrets Manager for production
- Audit secret access - Log when secrets are accessed or rotated
Troubleshooting
Variable Not Loading
Check:
- Variable is defined in correct
.envfile - No typos in variable name
- No extra spaces around
= - Quotes used correctly (bash: no quotes for simple values)
- Docker Compose file references correct env_file
Debug:
# Print environment variables in container
docker exec <container> env | grep DB_
Connection String Issues
Test connection string format:
# Ensure semicolons separate components
# Ensure no trailing/leading spaces
# Ensure password special characters are not causing issues
Frontend Variables Not Accessible
Remember:
- Only
NEXT_PUBLIC_*variables are accessible in browser - Server-side variables require getServerSideProps or API routes
- Variables must be defined at build time for static pages
Example Configuration Files
.env.dev (Backend/Docker)
# Database
DB_SERVER=sqlserver,1433
DB_NAME=Biergarten
DB_USER=sa
DB_PASSWORD=Dev_Password_123!
# JWT
JWT_SECRET=development-secret-key-at-least-32-characters-long-recommended-longer
# Migration
CLEAR_DATABASE=true
# ASP.NET Core
ASPNETCORE_ENVIRONMENT=Development
ASPNETCORE_URLS=http://0.0.0.0:8080
# SQL Server Container
SA_PASSWORD=Dev_Password_123!
ACCEPT_EULA=Y
MSSQL_PID=Express
.env.local (Frontend)
# Base
BASE_URL=http://localhost:3000
NODE_ENV=development
# Authentication
CONFIRMATION_TOKEN_SECRET=<generated-with-openssl>
RESET_PASSWORD_TOKEN_SECRET=<generated-with-openssl>
SESSION_SECRET=<generated-with-openssl>
# Database (current Prisma setup)
POSTGRES_PRISMA_URL=postgresql://user:pass@db.neon.tech/biergarten?pgbouncer=true
POSTGRES_URL_NON_POOLING=postgresql://user:pass@db.neon.tech/biergarten
# External Services
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=my-cloud
CLOUDINARY_KEY=123456789012345
CLOUDINARY_SECRET=abcdefghijklmnopqrstuvwxyz
MAPBOX_ACCESS_TOKEN=pk.eyJ...
SPARKPOST_API_KEY=abc123...
SPARKPOST_SENDER_ADDRESS=noreply@biergarten.app
# Admin (for seeding)
ADMIN_PASSWORD=Admin_Dev_Password_123!