Environment Variables
Overview
This guide provides a comprehensive reference for all environment variables used in the Ink platform. Learn how to configure different environments, manage secrets securely, and understand variable precedence.
Target Audience: Developers and DevOps engineers
Prerequisites: Basic understanding of environment variables and shell configuration
Estimated Time: 20-30 minutes
Prerequisites
- Understanding of environment variables in Linux/macOS/Windows
- Basic shell scripting knowledge
- Familiarity with Docker and Docker Compose
- Knowledge of application configuration
Variable Categories
Installation Steps
1. Create Environment File
# filepath: /Users/jetstart/dev/jetrev/ink/.env
# Application Configuration
SPRING_PROFILES_ACTIVE=local
SERVER_PORT=8081
CONTEXT_PATH=/
LOG_LEVEL=INFO
HIBERNATE_LOG_LEVEL=WARN
SHOW_SQL=false
# Database Configuration
DB_URL=jdbc:postgresql://localhost:5432/ink_database
DB_USERNAME=ink_user
DB_PASSWORD=ink_password
DB_POOL_SIZE=10
DB_POOL_MIN_IDLE=5
# Security Configuration
JWT_SECRET=your-256-bit-secret-key-change-in-production
JWT_EXPIRATION=86400000
KEYCLOAK_ISSUER_URI=http://localhost:8080/realms/ink
KEYCLOAK_JWK_URI=http://localhost:8080/realms/ink/protocol/openid-connect/certs
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8081
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DATABASE=0
# Feature Flags
REGISTRATION_ENABLED=true
EMAIL_VERIFICATION_REQUIRED=false
TWO_FACTOR_ENABLED=false
RATE_LIMIT_ENABLED=true
RATE_LIMIT_RPM=100
# Logging
LOG_FILE=logs/ink-platform.log
2. Environment-Specific Files
# filepath: /Users/jetstart/dev/jetrev/ink/.env.local
# Local Development Environment
SPRING_PROFILES_ACTIVE=local
LOG_LEVEL=DEBUG
SHOW_SQL=true
HIBERNATE_LOG_LEVEL=DEBUG
REGISTRATION_ENABLED=true
EMAIL_VERIFICATION_REQUIRED=false
# filepath: /Users/jetstart/dev/jetrev/ink/.env.production
# Production Environment (DO NOT COMMIT)
SPRING_PROFILES_ACTIVE=prod
SERVER_PORT=8080
LOG_LEVEL=WARN
SHOW_SQL=false
DB_URL=jdbc:postgresql://prod-db-host:5432/ink_production
DB_USERNAME=${PROD_DB_USER}
DB_PASSWORD=${PROD_DB_PASSWORD}
DB_POOL_SIZE=50
JWT_SECRET=${PROD_JWT_SECRET}
KEYCLOAK_ISSUER_URI=https://auth.production.com/realms/ink
CORS_ALLOWED_ORIGINS=https://app.production.com
REDIS_HOST=prod-redis-host
REDIS_PASSWORD=${PROD_REDIS_PASSWORD}
EMAIL_VERIFICATION_REQUIRED=true
TWO_FACTOR_ENABLED=true
3. Docker Compose Integration
# filepath: /Users/jetstart/dev/jetrev/ink/docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_USER=${DB_USERNAME:-ink_user}
- POSTGRES_PASSWORD=${DB_PASSWORD:-ink_password}
- POSTGRES_DB=${POSTGRES_DB:-ink_database}
ports:
- "${DB_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
keycloak:
image: quay.io/keycloak/keycloak:23.0
environment:
- KEYCLOAK_ADMIN=${KEYCLOAK_ADMIN:-admin}
- KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD:-admin}
- KC_DB=postgres
- KC_DB_URL=${KEYCLOAK_DB_URL:-jdbc:postgresql://postgres:5432/keycloak}
- KC_DB_USERNAME=${DB_USERNAME:-ink_user}
- KC_DB_PASSWORD=${DB_PASSWORD:-ink_password}
command: start-dev
ports:
- "${KEYCLOAK_PORT:-8080}:8080"
depends_on:
- postgres
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD:-}
ports:
- "${REDIS_PORT:-6379}:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Configuration
Core Application Variables
| Variable | Default | Description | Required |
|---|---|---|---|
SPRING_PROFILES_ACTIVE | local | Active Spring profile | Yes |
SERVER_PORT | 8081 | Application server port | No |
CONTEXT_PATH | / | Application context path | No |
LOG_LEVEL | INFO | Application log level | No |
LOG_FILE | logs/ink-platform.log | Log file path | No |
Database Variables
| Variable | Default | Description | Required |
|---|---|---|---|
DB_URL | jdbc:postgresql://localhost:5432/ink_database | Database JDBC URL | Yes |
DB_USERNAME | ink_user | Database username | Yes |
DB_PASSWORD | ink_password | Database password | Yes |
DB_POOL_SIZE | 10 | Max connection pool size | No |
DB_POOL_MIN_IDLE | 5 | Minimum idle connections | No |
Security Variables
| Variable | Default | Description | Required |
|---|---|---|---|
JWT_SECRET | - | JWT signing secret (256-bit) | Yes |
JWT_EXPIRATION | 86400000 | JWT expiration (ms) | No |
KEYCLOAK_ISSUER_URI | http://localhost:8080/realms/ink | Keycloak issuer URI | Yes |
KEYCLOAK_JWK_URI | Auto-derived | Keycloak JWK set URI | No |
CORS_ALLOWED_ORIGINS | http://localhost:3000 | Allowed CORS origins | Yes |
Redis Variables
| Variable | Default | Description | Required |
|---|---|---|---|
REDIS_HOST | localhost | Redis host | Yes |
REDIS_PORT | 6379 | Redis port | No |
REDIS_PASSWORD | Empty | Redis password | No |
REDIS_DATABASE | 0 | Redis database index | No |
Feature Flag Variables
| Variable | Default | Description | Required |
|---|---|---|---|
REGISTRATION_ENABLED | true | Enable user registration | No |
EMAIL_VERIFICATION_REQUIRED | false | Require email verification | No |
TWO_FACTOR_ENABLED | false | Enable 2FA | No |
RATE_LIMIT_ENABLED | true | Enable rate limiting | No |
RATE_LIMIT_RPM | 100 | Requests per minute limit | No |
Usage Examples
Loading Environment Variables
# Load from .env file
export $(cat .env | xargs)
# Load from profile-specific file
export $(cat .env.local | xargs)
# Verify loaded variables
echo $SPRING_PROFILES_ACTIVE
echo $DB_URL
Running with Environment Variables
# Using export
export SPRING_PROFILES_ACTIVE=dev
export SERVER_PORT=8082
mvn spring-boot:run
# Inline variables
SPRING_PROFILES_ACTIVE=dev SERVER_PORT=8082 mvn spring-boot:run
# Using .env file with Docker
docker compose --env-file .env.local up -d
Programmatic Access
// filepath: /Users/jetstart/dev/jetrev/ink/src/main/java/com/jetrev/ink/config/EnvironmentConfig.java
@Configuration
public class EnvironmentConfig {
@Value("${SPRING_PROFILES_ACTIVE:local}")
private String activeProfile;
@Autowired
private Environment environment;
@Bean
public String getEnvironmentInfo() {
return String.format(
"Profile: %s, Port: %s",
activeProfile,
environment.getProperty("SERVER_PORT", "8081")
);
}
}
Verification
Verification Script
# filepath: /Users/jetstart/dev/jetrev/ink/scripts/verify-env.sh
#!/bin/bash
echo "Verifying environment variables..."
# Check required variables
REQUIRED_VARS=(
"SPRING_PROFILES_ACTIVE"
"DB_URL"
"DB_USERNAME"
"DB_PASSWORD"
"JWT_SECRET"
"KEYCLOAK_ISSUER_URI"
)
MISSING_VARS=()
for var in "${REQUIRED_VARS[@]}"; do
if [ -z "${!var}" ]; then
MISSING_VARS+=("$var")
else
echo "✓ $var is set"
fi
done
if [ ${#MISSING_VARS[@]} -ne 0 ]; then
echo ""
echo "❌ Missing required variables:"
printf ' - %s\n' "${MISSING_VARS[@]}"
exit 1
fi
echo ""
echo "✅ All required environment variables are set"
Testing Environment Configuration
// filepath: /Users/jetstart/dev/jetrev/ink/src/test/java/com/jetrev/ink/config/EnvironmentTest.java
@SpringBootTest
@ActiveProfiles("test")
class EnvironmentTest {
@Autowired
private Environment environment;
@Test
void testRequiredVariablesPresent() {
assertThat(environment.getProperty("spring.datasource.url"))
.isNotBlank();
assertThat(environment.getProperty("spring.datasource.username"))
.isNotBlank();
}
@Test
void testProfileConfiguration() {
String[] profiles = environment.getActiveProfiles();
assertThat(profiles).contains("test");
}
}
Troubleshooting
Variable Not Being Recognized
# Check if variable is exported
env | grep SPRING_PROFILES_ACTIVE
# Source .env file properly
set -a
source .env
set +a
# Verify in Spring Boot
curl http://localhost:8081/actuator/env | grep SPRING_PROFILES_ACTIVE
Docker Compose Variable Issues
# Verify Docker Compose reads .env
docker compose config
# Check specific service environment
docker compose exec app env | grep DB_URL
# Debug variable expansion
docker compose --env-file .env.local config
Precedence Conflicts
// filepath: /Users/jetstart/dev/jetrev/ink/src/main/java/com/jetrev/ink/debug/VariableDebugger.java
@Component
@Slf4j
public class VariableDebugger implements ApplicationRunner {
@Autowired
private Environment environment;
@Override
public void run(ApplicationArguments args) {
log.info("=== Variable Sources ===");
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
MutablePropertySources sources = env.getPropertySources();
sources.forEach(source -> {
log.info("Source: {} ({})", source.getName(), source.getClass().getSimpleName());
});
// Check specific property
String dbUrl = environment.getProperty("spring.datasource.url");
log.info("Final DB URL: {}", dbUrl);
}
}
Code Examples
Environment-Based Bean Configuration
// filepath: /Users/jetstart/dev/jetrev/ink/src/main/java/com/jetrev/ink/config/EnvBasedConfig.java
@Configuration
public class EnvBasedConfig {
@Value("${SPRING_PROFILES_ACTIVE:local}")
private String profile;
@Bean
@ConditionalOnProperty(name = "REGISTRATION_ENABLED", havingValue = "true")
public RegistrationService registrationService() {
return new RegistrationService();
}
@Bean
public DataSource dataSource(
@Value("${DB_URL}") String url,
@Value("${DB_USERNAME}") String username,
@Value("${DB_PASSWORD}") String password,
@Value("${DB_POOL_SIZE:10}") int poolSize) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(poolSize);
return new HikariDataSource(config);
}
}
Secure Variable Handling
// filepath: /Users/jetstart/dev/jetrev/ink/src/main/java/com/jetrev/ink/security/SecretValidator.java
@Component
public class SecretValidator implements InitializingBean {
@Value("${JWT_SECRET}")
private String jwtSecret;
@Value("${SPRING_PROFILES_ACTIVE}")
private String profile;
@Override
public void afterPropertiesSet() {
// Validate JWT secret length
if (jwtSecret.length() < 32) {
throw new IllegalStateException(
"JWT_SECRET must be at least 32 characters");
}
// Warn about default secrets in production
if ("prod".equals(profile) &&
jwtSecret.contains("changeme")) {
throw new IllegalStateException(
"Default JWT_SECRET detected in production!");
}
}
}
Best Practices
- Never Commit Secrets: Add
.env*to.gitignore - Use Default Values: Provide sensible defaults for non-sensitive variables
- Validate Required Variables: Check presence on startup
- Document All Variables: Maintain up-to-date documentation
- Use Strong Secrets: Generate cryptographically secure values
- Rotate Credentials: Implement regular rotation for production
- Profile Separation: Keep profile-specific variables isolated
- Environment Isolation: Use different credentials per environment
.gitignore Configuration
# filepath: /Users/jetstart/dev/jetrev/ink/.gitignore
# ...existing code...
# Environment files
.env
.env.local
.env.*.local
.env.production
.env.staging
*.env
Secret Generation
# Generate JWT secret
openssl rand -base64 32
# Generate secure password
openssl rand -base64 24
# Generate UUID
uuidgen
Performance Optimization
Connection Pool Tuning
# High-traffic configuration
DB_POOL_SIZE=50
DB_POOL_MIN_IDLE=20
DB_POOL_MAX_LIFETIME=1800000
DB_POOL_CONNECTION_TIMEOUT=20000
Redis Optimization
# Production Redis settings
REDIS_MAX_ACTIVE=20
REDIS_MAX_IDLE=10
REDIS_MIN_IDLE=5
REDIS_MAX_WAIT=-1
Related Documentation
Additional Resources
Next Steps: Learn about Keycloak Integration for authentication and authorization.