Environment Setup
This guide covers comprehensive environment configuration for the Panels project, including automated environment file generation and multi-environment support.
Quick Start
# 1. Generate environment files from your compose.yaml
pnpm generate:env
# 2. Start infrastructure services
pnpm infra
# 3. Start development
pnpm devEnvironment File Generation
The project includes an intelligent environment generator that extracts configuration from your Docker Compose setup and creates appropriate .env files for each application.
Generated Files
| File | Purpose | Application |
|---|---|---|
apps/services/.env | Backend API configuration | Fastify server |
apps/app/.env.local | Frontend configuration | Next.js app |
.env.test | Testing environment | Test suites |
*.example | Template files (safe to commit) | Documentation |
Configuration Sources
The generator intelligently extracts settings from multiple sources:
From Docker Compose (compose.yaml)
- PostgreSQL: Database credentials, ports, connection strings
- Redis: Cache server credentials and connection details
- Medplum: Server URLs and client configuration
- Service Ports: All exposed port mappings
Environment-Specific Overrides
- Development: Local development optimizations
- Staging: Pre-production configurations
- Production: Production-ready settings with external services
Commands
# Generate development environment (default)
pnpm generate:env
# Generate staging environment
pnpm generate:env:staging
# Generate production environment
pnpm generate:env:prod
# Force overwrite existing files
pnpm generate:env -- --forceExample Output
When you run pnpm generate:env, you'll see:
🚀 Generating .env files for environment: development
📊 Extracted from compose.yaml:
DATABASE_USER=medplum
DATABASE_PASSWORD=medplum
DATABASE_PORT=5432
REDIS_PASSWORD=medplum
REDIS_PORT=6379
MEDPLUM_BASE_URL=http://localhost:8103
✅ Generated: apps/services/.env
Backend services configuration
✅ Generated: apps/app/.env.local
Frontend application configuration
✅ Generated: .env.test
Testing environment configuration
📝 Generating example files...
✅ Generated: apps/services/.env.example
✅ Generated: apps/app/.env.local.example
✅ Generated: .env.test.example
🎉 Environment file generation complete!Shared env for multiple worktrees
If you use Git worktrees and keep custom secrets or dev tokens in env files, those files are not shared across worktrees (they are untracked and ignored). You can keep one set of env files outside the repo and copy them into each new worktree.
Where to keep shared env files
Create a directory outside the repo (e.g. ~/panels-env) with the same layout as the repo:
~/panels-env/
apps/
app/
.env.local
services/
.envPopulate these files once (e.g. from your main worktree or from pnpm generate:env then move/copy them).
Copying into a new worktree
After creating a new worktree, from that worktree run:
# Option 1: environment variable
PANELS_ENV_SOURCE_DIR=~/panels-env pnpm env:copy
# Option 2: CLI flag
pnpm env:copy -- --source ~/panels-envTo overwrite existing env files in the worktree:
pnpm env:copy:force
# or with explicit source:
PANELS_ENV_SOURCE_DIR=~/panels-env pnpm env:copy -- --forceWhen to use env:copy vs generate:env
| Use case | Command |
|---|---|
First-time setup or single worktree; config can be generated from compose.yaml | pnpm generate:env |
| New worktree; you already have shared env files with secrets/tokens outside the repo | pnpm env:copy (with PANELS_ENV_SOURCE_DIR or --source) |
generate:env creates files from templates and Docker Compose. env:copy only copies from your external directory and does not modify your secrets.
Manual Configuration
If you prefer manual setup or need custom configuration:
Backend Services (.env)
# Server Configuration
NODE_ENV=development
PORT=3001
LOG_LEVEL=debug
# Database (PostgreSQL)
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=medplum
DATABASE_USER=medplum
DATABASE_PASSWORD=medplum
DATABASE_URL=postgresql://medplum:medplum@localhost:5432/medplum
# Cache (Redis)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=medplum
REDIS_URL=redis://:medplum@localhost:6379
# External Services
MEDPLUM_BASE_URL=http://localhost:8103
MEDPLUM_CLIENT_ID=2a4b77f2-4d4e-43c6-9b01-330eb5ca772f
# Security
JWT_SECRET=your-super-secret-jwt-key-change-in-production
# Multi-tenant
DEFAULT_TENANT_ID=tenant-dev
DEFAULT_USER_ID=user-dev
# API Configuration
API_BASE_URL=http://localhost:3001
CORS_ORIGINS=http://localhost:3000,http://localhost:3003Frontend App (.env.local)
# Environment
NODE_ENV=development
# API Configuration
API_URL=http://localhost:3001
MEDPLUM_BASE_URL=http://localhost:8103
# Authentication
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-nextauth-secret-change-in-production
# Feature Flags
ENABLE_DEBUG=true
APP_ENV=developmentTesting Environment (.env.test)
# Environment
NODE_ENV=test
# Test Database (separate from development)
DATABASE_URL=postgresql://medplum:medplum@localhost:5432/medplum_test
REDIS_URL=redis://:medplum@localhost:6379/1
# Test Services
API_BASE_URL=http://localhost:3001
MEDPLUM_BASE_URL=http://localhost:8103
# Test Secrets (not sensitive)
JWT_SECRET=test-jwt-secret
NEXTAUTH_SECRET=test-nextauth-secret
# Test Tenant
DEFAULT_TENANT_ID=tenant-test
DEFAULT_USER_ID=user-test
LOG_LEVEL=warnEnvironment-Specific Configurations
Development Environment
- Database: Local PostgreSQL via Docker Compose
- Redis: Local Redis via Docker Compose
- Medplum: Local Medplum server
- Logging: Debug level for detailed information
- Security: Development-friendly secrets
Staging Environment
- Database: Staging database (external)
- Redis: Staging cache (external)
- Medplum: Staging Medplum instance
- Logging: Info level
- Security: Staging-specific secrets
- URLs: Staging domain URLs
Production Environment
- Database: Production database (external, encrypted)
- Redis: Production cache (external, clustered)
- Medplum: Production Medplum instance
- Logging: Error and warning levels only
- Security: Production secrets (never commit)
- URLs: Production domain URLs
Security Best Practices
Development
- ✅ Use the generated development secrets
- ✅ Keep
.env.examplefiles in version control - ❌ Never commit actual
.envfiles
Staging/Production
- ✅ Use environment-specific secret management
- ✅ Rotate secrets regularly
- ✅ Use encrypted connections
- ❌ Never use development secrets in production
Troubleshooting
Common Issues
Q: Environment files not being loaded
# Check if files exist
ls -la apps/services/.env
ls -la apps/app/.env.local
# Regenerate if missing (from compose.yaml)
pnpm generate:env --forceQ: New worktree has no .env files If you use Git worktrees and keep env files outside the repo, copy them in after creating the worktree: PANELS_ENV_SOURCE_DIR=~/panels-env pnpm env:copy. See Shared env for multiple worktrees.
Q: Database connection errors
# Ensure infrastructure is running
pnpm infra
# Check PostgreSQL is healthy
docker ps | grep postgresQ: Redis connection errors
# Check Redis is running
docker ps | grep redis
# Test Redis connection
docker exec -it wl-redis redis-cli -a medplum pingQ: Port conflicts
# Check what's using the ports
lsof -i :3001 # API server
lsof -i :3000 # Frontend
lsof -i :5432 # PostgreSQL
lsof -i :6379 # RedisEnvironment Validation
You can validate your environment setup:
# Check all environment variables are loaded
pnpm --filter @panels/services dev --dry-run
# Test database connection
pnpm --filter @panels/services test:db-connection
# Test API endpoints
curl http://localhost:3001/healthIntegration with Development Workflow
The environment setup integrates seamlessly with your development workflow:
- Initial Setup:
pnpm generate:envcreates all necessary files - Infrastructure:
pnpm infrastarts all services with correct configuration - Development:
pnpm devuses the generated environment files automatically - Testing:
pnpm testuses the test environment configuration - Deployment: Environment-specific generation for different stages
Next Steps
- Create Your First Panel - Build your first panel using the configured environment
- Database Schema - Understand the MikroORM entities and schema
- API Integration - Learn how to interact with the API