|
| 1 | +# ADR-004: Configuration Approach - Files vs Environment Variables |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +Accepted |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +As part of the 12-Factor App refactoring (Phase 1), we need to decide how to handle |
| 10 | +application configuration for the Torrust Tracker Demo. There are two primary approaches: |
| 11 | + |
| 12 | +1. **File-based configuration**: Store configuration in template-generated files |
| 13 | + (e.g., `tracker.toml`) |
| 14 | +2. **Environment variable configuration**: Use environment variables for all |
| 15 | + configuration values |
| 16 | + |
| 17 | +Both approaches have trade-offs in terms of maintainability, deployment complexity, |
| 18 | +and operational flexibility. |
| 19 | + |
| 20 | +## Decision |
| 21 | + |
| 22 | +We will use a **hybrid approach** that prioritizes file-based configuration with |
| 23 | +selective use of environment variables: |
| 24 | + |
| 25 | +### File-based Configuration (Primary) |
| 26 | + |
| 27 | +- Application behavior settings |
| 28 | +- Port configurations |
| 29 | +- Policy settings (timeouts, intervals, etc.) |
| 30 | +- Feature flags (listed, private, stats enabled) |
| 31 | +- Non-sensitive defaults |
| 32 | + |
| 33 | +### Environment Variables (Secondary - Secrets & Environment-Specific Only) |
| 34 | + |
| 35 | +- Database credentials and connection strings |
| 36 | +- API tokens and authentication secrets |
| 37 | +- SSL certificates and keys |
| 38 | +- External IP addresses |
| 39 | +- Domain names |
| 40 | +- Infrastructure-specific settings |
| 41 | + |
| 42 | +## Rationale |
| 43 | + |
| 44 | +### Why File-based Configuration is Better for This Project |
| 45 | + |
| 46 | +#### 1. Project Scope and Purpose |
| 47 | + |
| 48 | +This repository is designed as an **automated installer/deployment tool** rather |
| 49 | +than a cloud-native, horizontally scalable application. The primary goal is to: |
| 50 | + |
| 51 | +- Deploy a single Torrust Tracker instance |
| 52 | +- Provide infrastructure automation |
| 53 | +- Enable easy manual maintenance post-deployment |
| 54 | + |
| 55 | +#### 2. Operational Advantages |
| 56 | + |
| 57 | +- **Easier maintenance**: Administrators can modify `tracker.toml` and restart the |
| 58 | + service without recreating containers |
| 59 | +- **Direct access**: System administrators can edit configuration files directly |
| 60 | + on the server |
| 61 | +- **Faster iteration**: Configuration changes don't require container recreation, |
| 62 | + only service restart |
| 63 | +- **Simpler troubleshooting**: All non-secret configuration is visible in |
| 64 | + human-readable files |
| 65 | + |
| 66 | +#### 3. Deployment Simplicity |
| 67 | + |
| 68 | +- **Fewer environment variables**: Reduces complexity in Docker Compose and |
| 69 | + deployment scripts |
| 70 | +- **Cleaner compose.yaml**: Environment sections remain minimal and focused on secrets |
| 71 | +- **Reduced coupling**: Application configuration is decoupled from container |
| 72 | + orchestration |
| 73 | + |
| 74 | +#### 4. Administrative Experience |
| 75 | + |
| 76 | +- **Familiar patterns**: System administrators expect to find configuration in files |
| 77 | + like `/etc/torrust/tracker/tracker.toml` |
| 78 | +- **Documentation alignment**: Configuration files can be documented and versioned |
| 79 | + alongside code |
| 80 | +- **Backup friendly**: Configuration files are easier to backup and restore as part |
| 81 | + of standard system administration |
| 82 | + |
| 83 | +### When Environment Variables Are Appropriate |
| 84 | + |
| 85 | +#### 1. Secrets Management |
| 86 | + |
| 87 | +```bash |
| 88 | +# Database credentials |
| 89 | +MYSQL_ROOT_PASSWORD=secret_password |
| 90 | +MYSQL_PASSWORD=user_password |
| 91 | + |
| 92 | +# API authentication |
| 93 | +TRACKER_ADMIN_TOKEN=admin_token_123 |
| 94 | + |
| 95 | +# Grafana admin credentials |
| 96 | +GF_SECURITY_ADMIN_PASSWORD=secure_password |
| 97 | +``` |
| 98 | + |
| 99 | +#### 2. Environment-Specific Values |
| 100 | + |
| 101 | +```bash |
| 102 | +# Network configuration that varies by deployment |
| 103 | +EXTERNAL_IP=192.168.1.100 |
| 104 | +DOMAIN_NAME=tracker.example.com |
| 105 | + |
| 106 | +# Infrastructure differences |
| 107 | +ON_REVERSE_PROXY=true |
| 108 | +LOG_LEVEL=info |
| 109 | +``` |
| 110 | + |
| 111 | +#### 3. Container Runtime Configuration |
| 112 | + |
| 113 | +```bash |
| 114 | +# Docker-specific settings |
| 115 | +USER_ID=1000 |
| 116 | +MYSQL_DATABASE=torrust_tracker |
| 117 | +``` |
| 118 | + |
| 119 | +## Implementation Examples |
| 120 | + |
| 121 | +### **File-based Configuration** (`tracker.toml`) |
| 122 | + |
| 123 | +```toml |
| 124 | +[metadata] |
| 125 | +app = "torrust-tracker" |
| 126 | +purpose = "configuration" |
| 127 | +schema_version = "2.0.0" |
| 128 | + |
| 129 | +[logging] |
| 130 | +threshold = "debug" # Environment-specific value |
| 131 | + |
| 132 | +[core] |
| 133 | +inactive_peer_cleanup_interval = 600 |
| 134 | +listed = false |
| 135 | +private = false |
| 136 | +tracker_usage_statistics = true |
| 137 | + |
| 138 | +[core.announce_policy] |
| 139 | +interval = 120 |
| 140 | +interval_min = 120 |
| 141 | + |
| 142 | +[core.database] |
| 143 | +driver = "mysql" |
| 144 | +# URL set via environment variable at runtime |
| 145 | +url = "" |
| 146 | + |
| 147 | +[core.net] |
| 148 | +external_ip = "0.0.0.0" |
| 149 | +on_reverse_proxy = false # Environment-specific value |
| 150 | + |
| 151 | +[core.tracker_policy] |
| 152 | +max_peer_timeout = 900 |
| 153 | +persistent_torrent_completed_stat = false |
| 154 | +remove_peerless_torrents = true |
| 155 | + |
| 156 | +# Admin token set via environment variable at runtime |
| 157 | +[http_api.access_tokens] |
| 158 | +# admin = "" |
| 159 | + |
| 160 | +[[udp_trackers]] |
| 161 | +bind_address = "0.0.0.0:6868" |
| 162 | + |
| 163 | +[[udp_trackers]] |
| 164 | +bind_address = "0.0.0.0:6969" |
| 165 | + |
| 166 | +[[http_trackers]] |
| 167 | +bind_address = "0.0.0.0:7070" |
| 168 | +``` |
| 169 | + |
| 170 | +### **Environment Variables** (`.env`) |
| 171 | + |
| 172 | +```bash |
| 173 | +# Secrets only |
| 174 | +MYSQL_ROOT_PASSWORD=secret_root_password |
| 175 | +MYSQL_PASSWORD=secret_user_password |
| 176 | +TRACKER_ADMIN_TOKEN=admin_secret_token |
| 177 | + |
| 178 | +# Docker runtime |
| 179 | +USER_ID=1000 |
| 180 | +MYSQL_DATABASE=torrust_tracker |
| 181 | +MYSQL_USER=torrust |
| 182 | + |
| 183 | +# Grafana admin |
| 184 | +GF_SECURITY_ADMIN_USER=admin |
| 185 | +GF_SECURITY_ADMIN_PASSWORD=admin_password |
| 186 | +``` |
| 187 | + |
| 188 | +## Benefits |
| 189 | + |
| 190 | +### **For System Administrators** |
| 191 | + |
| 192 | +- Configuration changes are made in familiar file locations |
| 193 | +- No need to understand Docker environment variable injection |
| 194 | +- Standard Unix administration patterns apply |
| 195 | +- Easy to backup and restore configurations |
| 196 | + |
| 197 | +### **For Developers** |
| 198 | + |
| 199 | +- Cleaner separation of concerns |
| 200 | +- Fewer template variables to manage |
| 201 | +- Simpler Docker Compose files |
| 202 | +- Easier testing and validation |
| 203 | + |
| 204 | +### **For Operations** |
| 205 | + |
| 206 | +- Faster configuration updates (restart vs recreate) |
| 207 | +- Better debugging capabilities |
| 208 | +- Standard logging and monitoring patterns |
| 209 | +- Familiar deployment patterns |
| 210 | + |
| 211 | +## Trade-offs |
| 212 | + |
| 213 | +### **What We Give Up** |
| 214 | + |
| 215 | +- **Cloud-native patterns**: Less suitable for Kubernetes or other orchestrators |
| 216 | +- **Dynamic reconfiguration**: Cannot change configuration without file access |
| 217 | +- **Secret injection**: Some secrets still appear in config files (but only connection |
| 218 | + strings, not raw credentials) |
| 219 | + |
| 220 | +### **What We Gain** |
| 221 | + |
| 222 | +- **Operational simplicity**: Standard system administration patterns |
| 223 | +- **Deployment reliability**: Fewer moving parts in the deployment process |
| 224 | +- **Administrative control**: Direct access to configuration without container knowledge |
| 225 | +- **Performance**: No environment variable processing overhead |
| 226 | + |
| 227 | +## Exceptions |
| 228 | + |
| 229 | +### **Prometheus Configuration** |
| 230 | + |
| 231 | +Prometheus does not support runtime environment variable substitution in its configuration |
| 232 | +files. Therefore, API tokens for scraping Torrust Tracker metrics must be embedded in |
| 233 | +the `prometheus.yml` file during template generation: |
| 234 | + |
| 235 | +```yaml |
| 236 | +scrape_configs: |
| 237 | + - job_name: "torrust-tracker-stats" |
| 238 | + static_configs: |
| 239 | + - targets: ["tracker:1212"] |
| 240 | + metrics_path: "/api/v1/stats" |
| 241 | + params: |
| 242 | + token: ["admin_token_123"] # Token embedded at generation time |
| 243 | + format: ["prometheus"] |
| 244 | +``` |
| 245 | +
|
| 246 | +This is an acceptable exception because: |
| 247 | +
|
| 248 | +- Prometheus config files are not typically edited by administrators |
| 249 | +- The token is only for internal monitoring within the Docker network |
| 250 | +- The configuration is regenerated when environment changes |
| 251 | +
|
| 252 | +## Consequences |
| 253 | +
|
| 254 | +### **Configuration Management Process** |
| 255 | +
|
| 256 | +1. **Environment-specific values**: Set in `infrastructure/config/environments/{environment}.env` |
| 257 | +2. **Template processing**: Generate config files using `configure-env.sh` |
| 258 | +3. **Validation**: Validate generated configurations using `validate-config.sh` |
| 259 | +4. **Deployment**: Deploy with file-based configurations |
| 260 | + |
| 261 | +### **Maintenance Workflow** |
| 262 | + |
| 263 | +1. **For secrets**: Update `.env` file and restart containers |
| 264 | +2. **For behavior**: Edit `tracker.toml` and restart tracker service |
| 265 | +3. **For infrastructure**: Update templates and regenerate configurations |
| 266 | + |
| 267 | +### **Future Considerations** |
| 268 | + |
| 269 | +- If the project evolves toward cloud-native deployment, this decision can be revisited |
| 270 | +- Environment variable overrides can be added later without breaking existing deployments |
| 271 | +- The hybrid approach provides flexibility for future architectural changes |
| 272 | + |
| 273 | +## Alternatives Considered |
| 274 | + |
| 275 | +### **Full Environment Variable Approach** |
| 276 | + |
| 277 | +- **Pros**: Cloud-native, 12-factor compliant, dynamic configuration |
| 278 | +- **Cons**: Complex Docker Compose, harder maintenance, container recreation required |
| 279 | + |
| 280 | +### **Full File-based Approach** |
| 281 | + |
| 282 | +- **Pros**: Maximum simplicity, traditional Unix patterns |
| 283 | +- **Cons**: Secrets in files, harder automation, less secure |
| 284 | + |
| 285 | +### **External Configuration Service** |
| 286 | + |
| 287 | +- **Pros**: Centralized management, audit trails, dynamic updates |
| 288 | +- **Cons**: Additional infrastructure, complexity overkill for single-instance deployment |
| 289 | + |
| 290 | +## Related Decisions |
| 291 | + |
| 292 | +- [ADR-002: Docker for All Services](002-docker-for-all-services.md) - Establishes container-based deployment |
| 293 | +- [ADR-003: Use MySQL Over MariaDB](003-use-mysql-over-mariadb.md) - Database choice |
| 294 | + affects connection configuration |
| 295 | + |
| 296 | +## References |
| 297 | + |
| 298 | +- [The Twelve-Factor App](https://12factor.net/config) |
| 299 | +- [Torrust Tracker Configuration Documentation](https://docs.rs/torrust-tracker) |
| 300 | +- [Docker Compose Environment Variables](https://docs.docker.com/compose/environment-variables/) |
0 commit comments