Configuration Guide¶
Complete guide to configuring the Go REST API Boilerplate (GRAB) with the Viper-based configuration system.
📋 Overview¶
The application uses a layered configuration system powered by Viper that supports multiple configuration sources with clear precedence rules.
Configuration Precedence (Highest to Lowest)¶
1. Environment Variables (.env file)
↓ (overrides)
2. Environment-specific Config Files (config.{environment}.yaml)
↓ (overrides)
3. Base Config File (config.yaml)
↓ (overrides)
4. Default Values (built into application)
This means environment variables always win, followed by environment-specific config files, then the base config file, and finally built-in defaults.
🗂️ Configuration Files¶
Base Configuration (configs/config.yaml)¶
The main configuration file with all available options and documentation:
# ===========================================
# Go REST API Boilerplate - Base Configuration
# ===========================================
app:
name: "GRAB API" # Application name
environment: "development" # Environment: development|staging|production
debug: true # Enable debug mode
database:
host: "db" # Database host
port: 5432 # Database port
user: "postgres" # Database user
password: "" # Database password (set via env var)
name: "grab" # Database name
sslmode: "disable" # SSL mode: disable|require|verify-ca|verify-full
jwt:
secret: "" # JWT secret (required, set via env var)
ttlhours: 24 # JWT token TTL in hours
server:
port: "8080" # Server port
readtimeout: 10 # Read timeout in seconds
writetimeout: 10 # Write timeout in seconds
idletimeout: 120 # Idle timeout in seconds
shutdowntimeout: 30 # Graceful shutdown timeout in seconds
maxheaderbytes: 1048576 # Max header bytes (1MB)
logging:
level: "info" # Log level: debug|info|warn|error
ratelimit:
enabled: false # Enable rate limiting
requests: 100 # Max requests per window
window: "1m" # Time window (e.g., "1m", "1h", "30s")
Environment-Specific Configuration¶
The application automatically loads environment-specific config files based on the APP_ENVIRONMENT variable:
Development (configs/config.development.yaml)¶
app:
name: "GRAB API (development)"
environment: "development"
debug: true
database:
host: "db"
password: "postgres" # Default for development
sslmode: "disable" # SSL disabled for convenience
jwt:
# Generate: make generate-jwt-secret
ttlhours: 24
Production (configs/config.production.yaml)¶
app:
name: "GRAB API"
environment: "production"
debug: false # Disable debug in production
database:
password: "" # Must be set via DATABASE_PASSWORD
sslmode: "require" # SSL required in production
jwt:
# Generate: make generate-jwt-secret
ttlhours: 24
Staging (configs/config.staging.yaml)¶
app:
name: "GRAB API (staging)"
environment: "staging"
debug: false
database:
sslmode: "require" # SSL required in staging
jwt:
# Generate: make generate-jwt-secret
ttlhours: 24
🌍 Environment Variables¶
Complete Reference¶
All configuration values can be overridden with environment variables using the format: SECTION_KEY=value.
Application Configuration¶
| Variable | Default | Description | Accepted Values |
|---|---|---|---|
APP_NAME | "GRAB API" | Application name | Any string |
APP_ENVIRONMENT | "development" | Runtime environment | development, staging, production |
APP_DEBUG | true | Enable debug mode | true, false |
Database Configuration¶
| Variable | Default | Description | Accepted Values |
|---|---|---|---|
DATABASE_HOST | "db" | Database host | Hostname or IP |
DATABASE_PORT | 5432 | Database port | 1-65535 |
DATABASE_USER | "postgres" | Database user | Any string |
DATABASE_PASSWORD | "" | Database password | Any string (required in production) |
DATABASE_NAME | "grab" | Database name | Valid PostgreSQL database name |
DATABASE_SSLMODE | "disable" | SSL mode | disable, require, verify-ca, verify-full |
JWT Configuration¶
CRITICAL SECURITY REQUIREMENT: JWT_SECRET must be set and secure.
| Variable | Default | Description | Accepted Values |
|---|---|---|---|
JWT_SECRET | "" (REQUIRED) | JWT signing secret | Min 32 chars (64+ for production). Must be cryptographically random. |
JWT_ACCESS_TOKEN_TTL | "15m" | Access token lifetime | Duration string (e.g., "15m", "1h") |
JWT_REFRESH_TOKEN_TTL | "168h" | Refresh token lifetime (7 days) | Duration string (e.g., "168h", "7d") |
JWT_TTLHOURS | 24 | Token TTL in hours (deprecated) | Positive integer |
Security Notes:
- ✅ Generate secure secret:
make generate-jwt-secret - ✅ Auto-generated:
make quick-startcreates one automatically if missing - ❌ Rejected patterns: "secret", "password", "test", "dev", "123456", etc.
- ⚠️ All environments require: 32+ characters minimum
Server Configuration¶
| Variable | Default | Description | Accepted Values |
|---|---|---|---|
SERVER_PORT | "8080" | Server port | "1" to "65535" |
SERVER_READTIMEOUT | 10 | Read timeout (seconds) | Non-negative integer |
SERVER_WRITETIMEOUT | 10 | Write timeout (seconds) | Non-negative integer |
SERVER_IDLETIMEOUT | 120 | Idle timeout (seconds) | Non-negative integer |
SERVER_SHUTDOWNTIMEOUT | 30 | Graceful shutdown timeout (seconds) | Non-negative integer |
SERVER_MAXHEADERBYTES | 1048576 | Max header bytes (1MB) | Non-negative integer |
Logging Configuration¶
| Variable | Default | Description | Accepted Values |
|---|---|---|---|
LOGGING_LEVEL | "info" | Log level | debug, info, warn, error |
Rate Limiting Configuration¶
| Variable | Default | Description | Accepted Values |
|---|---|---|---|
RATELIMIT_ENABLED | false | Enable rate limiting | true, false |
RATELIMIT_REQUESTS | 100 | Max requests per window | Positive integer |
RATELIMIT_WINDOW | "1m" | Time window | Go duration format (1s, 1m, 1h) |
🔧 Configuration Examples¶
Example 1: Development Setup¶
Create .env file:
# Development environment
APP_ENVIRONMENT=development
APP_DEBUG=true
# Database (Docker setup)
DATABASE_HOST=db
DATABASE_PASSWORD=postgres
# JWT (development only - use stronger secret in production)
JWT_SECRET=dev-secret-change-in-production
# Logging
LOGGING_LEVEL=debug
Example 2: Production Setup¶
Create .env file:
# Production environment
APP_ENVIRONMENT=production
APP_DEBUG=false
# Database (production values)
DATABASE_HOST=prod-db.example.com
DATABASE_PASSWORD=super-secure-password
DATABASE_SSLMODE=require
# JWT (strong production secret)
JWT_SECRET=this-is-a-very-strong-production-jwt-secret-that-is-at-least-32-characters-long
JWT_TTLHOURS=1
# Server
SERVER_PORT=8080
SERVER_READTIMEOUT=30
SERVER_WRITETIMEOUT=30
SERVER_IDLETIMEOUT=120
SERVER_SHUTDOWNTIMEOUT=30
SERVER_MAXHEADERBYTES=1048576
# Logging
LOGGING_LEVEL=warn
# Rate Limiting
RATELIMIT_ENABLED=true
RATELIMIT_REQUESTS=60
RATELIMIT_WINDOW=1m
Example 3: Docker Compose Override¶
# docker-compose.override.yml
version: '3.8'
services:
app:
environment:
- APP_ENVIRONMENT=staging
- DATABASE_HOST=staging-db
- JWT_SECRET=staging-jwt-secret-32-chars-min
- LOGGING_LEVEL=info
- RATELIMIT_ENABLED=true
⚡ Quick Setup Commands¶
Copy and Configure¶
Verify Configuration¶
# Check if app starts correctly
make up
# Check health endpoint
curl http://localhost:8080/health
# View configuration (debug info)
docker logs go-rest-api-boilerplate_app_1 | grep -i config
🔒 Security Best Practices¶
Production Configuration¶
-
Always use environment variables for secrets:
-
Use strong JWT secrets (32+ characters):
-
Enable SSL for database connections:
-
Use appropriate log levels:
Environment Isolation¶
- Development: Use
config.development.yaml+.env - Staging: Use
config.staging.yaml+ environment variables - Production: Use
config.production.yaml+ secure environment variables
🐛 Troubleshooting¶
Common Configuration Issues¶
1. Configuration Not Loading¶
Problem: Changes to config files not being applied.
Solution:
# Check which config file is being loaded
APP_ENVIRONMENT=development go run cmd/server/main.go
# Verify file exists
ls -la configs/config.development.yaml
# Check environment variable
echo $APP_ENVIRONMENT
2. Environment Variables Not Working¶
Problem: Environment variables not overriding config files.
Solution:
# Check variable format (must use underscores)
DATABASE_HOST=localhost # ✅ Correct
database.host=localhost # ❌ Wrong
# Check if .env file is loaded
cat .env | grep DATABASE_HOST
# Restart application after changes
make restart
3. Validation Errors¶
Problem: Application fails to start with validation errors.
Solutions:
JWT Secret Missing:
# Error: JWT secret is required
JWT_SECRET=your-secret-here
# Production: minimum 32 characters required
JWT_SECRET=this-is-a-32-character-secret-key
Database Password Missing in Production:
Invalid SSL Mode in Production:
4. Port Already in Use¶
Problem: Server port is already occupied.
Solution:
# Check what's using the port
lsof -i :8080
# Use different port
SERVER_PORT=8081
# Or kill the process
lsof -ti:8080 | xargs kill -9
📚 Advanced Configuration¶
Custom Configuration Loading¶
For advanced use cases, you can load configuration programmatically:
package main
import (
"github.com/vahiiiid/go-rest-api-boilerplate/internal/config"
)
func main() {
// Load from specific file
cfg, err := config.LoadConfig("/path/to/custom-config.yaml")
if err != nil {
log.Fatal(err)
}
// Load with environment detection
cfg, err = config.LoadConfig("") // Uses APP_ENVIRONMENT
if err != nil {
log.Fatal(err)
}
}
Testing Configuration¶
// Use test helper for consistent test config
func TestMyFunction(t *testing.T) {
cfg := config.NewTestConfig()
// cfg is now pre-configured for testing
}
🔗 Related Documentation¶
- Setup Guide - Initial configuration setup
- Development Guide - Using config in code
- Docker Guide - Docker-specific configuration
- Quick Reference - Configuration commands