Epic: Add Hetzner Provider Support
Parent: #1 - Project Roadmap
Roadmap Section: 2.1 - Add Hetzner provider support
Feature Documentation: docs/features/hetzner-provider-support/
Overview
This epic tracks the implementation of Hetzner Cloud as the first production infrastructure provider for the Torrust Tracker Deployer.
Background
The Torrust Tracker Deployer currently uses LXD as the sole infrastructure provider. LXD was chosen for development and local testing because:
- Fast VM creation (good for E2E tests)
- No cloud costs for development
- Works well in CI environments (GitHub Actions)
- Full VM isolation matching production behavior
However, for actual production deployments (like the Torrust Tracker Demo), we need cloud provider support. Hetzner Cloud was selected as the first production provider because:
- Cost-effective for the Torrust project
- Good European presence (relevant for torrent infrastructure)
- Simple API with good Terraform/OpenTofu support
- Established provider with proven reliability
Problem Statement
- Provider is hardcoded: The current implementation assumes LXD everywhere, with no way for users to select a different provider.
- Production deployments are blocked: Cannot deploy Torrust Tracker to cloud infrastructure using this tool.
- Provider-specific configuration is implicit: LXD-specific parameters (instance name, profile name) are embedded in the domain layer without clear provider context.
Approach: "Refactor First, Then Extend"
The implementation follows a two-phase approach:
-
Phase 1 - Make LXD Explicit: Refactor the current code to treat LXD as one of potentially many providers, even though it's currently the only one. This makes the codebase ready for extension.
-
Phase 2 - Add Hetzner: Implement Hetzner support by adding new templates and minimal code changes.
This approach is preferred because:
- Separates architectural changes from new functionality
- Allows validating the architecture before adding complexity
- Makes code reviews easier (smaller, focused PRs)
- Reduces risk of introducing bugs
Key Design Decisions
- No provider abstraction layer: Each provider uses its own native configuration (Terraform/OpenTofu variables) without a common abstraction
- Provider selection via environment config: User specifies provider in the environment JSON file (no default - always explicit)
- OpenTofu templates per provider: Separate template directories for each provider (
tofu/lxd/, tofu/hetzner/)
- Rust-based validation (MVP): Serde tagged enums handle provider-specific validation
- Single-use tool philosophy: All config (including API tokens) stored in environment JSON
- OS-level firewall only: Use UFW instead of provider firewalls for portability
InstanceName generic, ProfileName LXD-specific: Instance name uses strictest validation across all providers
- Local Terraform state only: No remote state backends
Tasks
Phase 1: Make LXD Explicit
This phase refactors the codebase to make LXD an explicit, selectable provider. Tasks are ordered by dependency.
Task 1: Add Provider enum and ProviderConfig types (#206)
Issue: #206
Dependencies: None (foundational task)
Create the foundational types:
Provider enum in domain layer (src/domain/provider/)
ProviderConfig, LxdConfig, HetznerConfig (domain types with validated fields)
ProviderSection, LxdProviderSection, HetznerProviderSection (application layer config types for JSON parsing)
Task 2: Update UserInputs to use ProviderConfig (#207)
Issue: #207
Dependencies: Task 1
Refactor the domain UserInputs struct:
- Add
provider_config: ProviderConfig field
- Move
profile_name from global field to LxdConfig
- Keep
instance_name as global (all providers need it)
- Update all code that accesses
profile_name
Task 3: Update EnvironmentCreationConfig (#208)
Issue: #208
Dependencies: Task 1
Update the application layer config type to include provider configuration:
- Add
provider section to EnvironmentCreationConfig
- Update
to_environment_params() to handle provider config
- Update JSON schema/examples in documentation
Task 4: Parameterize TofuTemplateRenderer by provider
Issue: TBD
Dependencies: Task 2
Make the template renderer provider-aware:
- Add
provider field to TofuTemplateRenderer
- Change hardcoded
tofu/lxd path to dynamic tofu/{provider}
- Update
EnvironmentContext::tofu_build_dir() to use provider
Task 5: Update environment JSON files and E2E tests
Issue: TBD
Dependencies: Tasks 2, 3
Update all configuration files to the new format:
- Update
data/e2e-*/environment.json files
- Update
data/my-environment/environment.json
- Ensure all E2E tests pass with new config format
- Add clear error message when
provider field is missing
Task 6: Update user documentation
Issue: TBD
Dependencies: Tasks 1-5
Document the provider selection feature:
- Update user guide with provider configuration
- Update example configurations
- Document migration from old format (if needed)
Phase 1 Dependency Graph
Task 1: Provider types (foundation)
│
├──► Task 2: Update UserInputs (domain)
│ │
│ └──► Task 4: Parameterize TofuTemplateRenderer
│ │
└──► Task 3: Update EnvironmentCreationConfig (application)
│
└──► Task 5: Update JSON files & E2E tests
│
└──► Task 6: Update documentation
Phase 2: Add Hetzner Provider
| Task |
Description |
Status |
| Create static Hetzner OpenTofu config |
Test manually before Tera templating |
Not Started |
| Add HetznerConfig to ProviderConfig enum |
Domain type for Hetzner-specific config |
Not Started |
| Convert OpenTofu files to Tera templates |
Create templates/tofu/hetzner/ directory |
Not Started |
| Add Hetzner variables template wrapper |
Infrastructure layer template handling |
Not Started |
| Create example Hetzner environment |
Example configuration for users |
Not Started |
| Add Hetzner-specific documentation |
Setup guide and troubleshooting |
Not Started |
| Manual testing with Hetzner |
Full deployment workflow testing |
Not Started |
| Add unit tests |
Config parsing and validation tests |
Not Started |
Phase 3: Documentation and Polish
| Task |
Description |
Status |
| User guide for Hetzner setup |
Step-by-step guide |
Not Started |
| Provider comparison documentation |
LXD vs Hetzner comparison |
Not Started |
| Cost estimation guidance |
Help users estimate Hetzner costs |
Not Started |
| Troubleshooting guide |
Common issues and solutions |
Not Started |
Definition of Done
Phase 1 Requirements
Phase 2 Requirements
Technical Requirements
Testing Requirements
Reference Implementation
A working Hetzner Terraform configuration exists in the bash Proof of Concept:
This PoC implementation provides:
main.tf - Hetzner firewall and server resources with cloud-init
variables.tf - Server type/location/image definitions with validation
outputs.tf - Standard output interface compatible with our parser
versions.tf - Required provider versions (hcloud ~> 1.47)
Related Documentation
Notes
- Important Context: This is a single-use deployment tool, not a management platform or sysadmin replacement. The tool automates initial Torrust Tracker deployment but is not intended for ongoing infrastructure management.
- The entire local configuration (
data/, envs/, build/ directories) is considered sensitive.
- Hetzner API token is provided by the product owner for manual testing.
Created: December 1, 2025
Status: Planning Complete - Ready for Implementation
Epic: Add Hetzner Provider Support
Parent: #1 - Project Roadmap
Roadmap Section: 2.1 - Add Hetzner provider support
Feature Documentation: docs/features/hetzner-provider-support/
Overview
This epic tracks the implementation of Hetzner Cloud as the first production infrastructure provider for the Torrust Tracker Deployer.
Background
The Torrust Tracker Deployer currently uses LXD as the sole infrastructure provider. LXD was chosen for development and local testing because:
However, for actual production deployments (like the Torrust Tracker Demo), we need cloud provider support. Hetzner Cloud was selected as the first production provider because:
Problem Statement
Approach: "Refactor First, Then Extend"
The implementation follows a two-phase approach:
Phase 1 - Make LXD Explicit: Refactor the current code to treat LXD as one of potentially many providers, even though it's currently the only one. This makes the codebase ready for extension.
Phase 2 - Add Hetzner: Implement Hetzner support by adding new templates and minimal code changes.
This approach is preferred because:
Key Design Decisions
tofu/lxd/,tofu/hetzner/)InstanceNamegeneric,ProfileNameLXD-specific: Instance name uses strictest validation across all providersTasks
Phase 1: Make LXD Explicit
This phase refactors the codebase to make LXD an explicit, selectable provider. Tasks are ordered by dependency.
Task 1: Add Provider enum and ProviderConfig types (#206)
Issue: #206
Dependencies: None (foundational task)
Create the foundational types:
Providerenum in domain layer (src/domain/provider/)ProviderConfig,LxdConfig,HetznerConfig(domain types with validated fields)ProviderSection,LxdProviderSection,HetznerProviderSection(application layer config types for JSON parsing)Task 2: Update UserInputs to use ProviderConfig (#207)
Issue: #207
Dependencies: Task 1
Refactor the domain
UserInputsstruct:provider_config: ProviderConfigfieldprofile_namefrom global field toLxdConfiginstance_nameas global (all providers need it)profile_nameTask 3: Update EnvironmentCreationConfig (#208)
Issue: #208
Dependencies: Task 1
Update the application layer config type to include provider configuration:
providersection toEnvironmentCreationConfigto_environment_params()to handle provider configTask 4: Parameterize TofuTemplateRenderer by provider
Issue: TBD
Dependencies: Task 2
Make the template renderer provider-aware:
providerfield toTofuTemplateRenderertofu/lxdpath to dynamictofu/{provider}EnvironmentContext::tofu_build_dir()to use providerTask 5: Update environment JSON files and E2E tests
Issue: TBD
Dependencies: Tasks 2, 3
Update all configuration files to the new format:
data/e2e-*/environment.jsonfilesdata/my-environment/environment.jsonproviderfield is missingTask 6: Update user documentation
Issue: TBD
Dependencies: Tasks 1-5
Document the provider selection feature:
Phase 1 Dependency Graph
Phase 2: Add Hetzner Provider
Phase 3: Documentation and Polish
Definition of Done
Phase 1 Requirements
provider: "lxd"fieldPhase 2 Requirements
provisioncommandconfigurecommanddestroycommandtestcommandTechnical Requirements
cargo run --bin linter all)./scripts/pre-commit.sh)Testing Requirements
Reference Implementation
A working Hetzner Terraform configuration exists in the bash Proof of Concept:
This PoC implementation provides:
main.tf- Hetzner firewall and server resources with cloud-initvariables.tf- Server type/location/image definitions with validationoutputs.tf- Standard output interface compatible with our parserversions.tf- Required provider versions (hcloud ~> 1.47)Related Documentation
Notes
data/,envs/,build/directories) is considered sensitive.Created: December 1, 2025
Status: Planning Complete - Ready for Implementation