Generate deployment artifacts without provisioning infrastructure.
Creates all deployment artifacts (OpenTofu, Ansible, Docker Compose, configuration files) for a deployment environment without actually provisioning infrastructure. This command is useful for:
- Previewing artifacts before committing to infrastructure provisioning
- Manual deployment workflows where you want to use generated artifacts with external tools
- Validation and inspection of what will be deployed
- Generating from config files without creating persistent environment state
# From existing environment (Created state)
torrust-tracker-deployer render --env-name <NAME> --instance-ip <IP> --output-dir <PATH>
# From configuration file (no environment creation)
torrust-tracker-deployer render --env-file <PATH> --instance-ip <IP> --output-dir <PATH>
# Overwrite existing output directory
torrust-tracker-deployer render --env-name <NAME> --instance-ip <IP> --output-dir <PATH> --force--env-name <NAME>- Name of existing environment in Created state--env-file <PATH>- Path to environment configuration file
Note: These options are mutually exclusive - use one or the other.
--instance-ip <IP>(required) - Target instance IP address for deployment--output-dir <PATH>(required) - Output directory for generated artifacts
The IP address is required because:
- In Created state, infrastructure doesn't exist yet (no real IP)
- With config file, no infrastructure is ever created
- IP is needed for Ansible inventory generation
The output directory is required to:
- Prevent conflicts with provision artifacts in
build/{env}/ - Enable preview without overwriting deployment artifacts
- Allow multiple renders with different IPs or configurations
- Clear separation between preview (render) and deployment (provision)
--force- Overwrite existing output directory (without this, command fails if directory exists)
- Environment created - Environment must exist in "Created" state
- Configuration valid - Environment configuration must be valid
- Configuration file - Valid environment configuration JSON file
- No environment needed - Does not require existing environment
- Templates available - Template files in
templates/directory - Write permissions - Ability to write to output directory
- Output directory - Must not exist (unless
--forcespecified)
[Created] --render--> [Created] (No state change)
Important: The render command is read-only - it never changes environment state.
When you render artifacts:
- Validates input - Checks environment exists or config file valid
- Parses configuration - Loads environment configuration
- Validates IP address - Ensures IP is in valid format (IPv4/IPv6)
- Renders templates - Generates all deployment artifacts:
- OpenTofu infrastructure code
- Ansible playbooks and inventory
- Docker Compose service definitions
- Tracker configuration (tracker.toml)
- Prometheus monitoring configuration
- Grafana dashboard provisioning
- Caddy reverse proxy configuration (if HTTPS enabled)
- Backup scripts (if backup enabled)
- Writes artifacts - Saves generated files to specified output directory
# Create environment
torrust-tracker-deployer create environment -f envs/my-config.json
# Preview artifacts with test IP in separate directory
torrust-tracker-deployer render --env-name my-env --instance-ip 192.168.1.100 --output-dir ./preview-my-env
# Review generated artifacts
ls -la preview-my-env/
# If satisfied, provision for real (writes to build/my-env/)
torrust-tracker-deployer provision my-env# Generate artifacts directly from config
torrust-tracker-deployer render \
--env-file envs/production.json \
--instance-ip 10.0.0.5 \
--output-dir /tmp/production-artifacts
# Artifacts in /tmp/production-artifacts/ (no environment created in data/)# Preview with different IPs
torrust-tracker-deployer render \
--env-name my-env \
--instance-ip 192.168.1.10 \
--output-dir ./preview-ip-10
torrust-tracker-deployer render \
--env-name my-env \
--instance-ip 10.0.1.20 \
--output-dir ./preview-ip-20
# Compare artifacts
diff -r preview-ip-10/ preview-ip-20/# Render artifacts
torrust-tracker-deployer render --env-name my-env --instance-ip 192.168.1.100 --output-dir ./inspect
# Check OpenTofu configuration
cat inspect/tofu/main.tf
# Check Ansible inventory (should show 192.168.1.100)
cat inspect/ansible/inventory.yml
# Check Docker Compose services
cat inspect/docker-compose/docker-compose.yml
# Check tracker configuration
cat inspect/tracker/tracker.tomlThe render command generates artifacts in the specified output directory:
<output-dir>/
├── tofu/ # Infrastructure as code
│ └── main.tf # OpenTofu configuration
├── ansible/ # Configuration management
│ ├── inventory.yml # Ansible inventory (with target IP)
│ └── playbooks/ # Ansible playbooks
├── docker-compose/ # Container orchestration
│ ├── docker-compose.yml # Service definitions
│ └── .env # Environment variables
├── tracker/ # Tracker configuration
│ └── tracker.toml # Tracker settings
├── prometheus/ # Metrics collection
│ └── prometheus.yml # Prometheus configuration
├── grafana/ # Visualization
│ ├── dashboards/ # Dashboard JSON files
│ └── provisioning/ # Datasources
├── caddy/ # Reverse proxy (if HTTPS enabled)
│ └── Caddyfile # Caddy configuration
└── backup/ # Backup (if enabled)
└── backup.sh # Backup script
- Ansible inventory (
ansible/inventory.yml) - Contains the target IP you specified - OpenTofu state - Infrastructure code ready for
tofu apply - Docker Compose - Complete service stack definition
- Configuration files - All service configurations rendered
Use --output-format json (or -o json) to get machine-readable output. Progress messages go to stderr; the JSON result goes to stdout.
torrust-tracker-deployer render \
--env-file envs/my-environment.json \
--instance-ip 192.168.1.100 \
--output-dir /tmp/build/my-environment \
--output-format json 2>/dev/null{
"environment_name": "my-environment",
"config_source": "Config file: envs/my-environment.json",
"target_ip": "192.168.1.100",
"output_dir": "/tmp/build/my-environment"
}| Field | Type | Description |
|---|---|---|
environment_name |
string | Name of the environment whose artifacts were generated |
config_source |
string | Description of the configuration source (env name or config file) |
target_ip |
string | IP address used in artifact generation |
output_dir |
string | Path to the directory containing generated artifacts |
# Create environment
cargo run -- create environment -f envs/staging.json
# Preview what will be deployed
cargo run -- render --env-name staging --instance-ip 203.0.113.10 --output-dir ./preview-staging
# Review artifacts in preview-staging/
# If satisfied, provision (writes to build/staging/)
cargo run -- provision stagingBenefit: Verify configuration correctness before committing to infrastructure costs.
# Generate artifacts without creating environment
cargo run -- render \
--env-file envs/production.json \
--instance-ip 203.0.113.50 \
--output-dir /tmp/manual-deploy
# Manually deploy using generated artifacts
cd /tmp/manual-deploy/tofu
tofu init
tofu apply
cd ../ansible
ansible-playbook -i inventory.yml deploy.ymlBenefit: Full control over deployment process using standard tools.
# Generate artifacts to validate configuration
cargo run -- render \
--env-file envs/test-config.json \
--instance-ip 192.168.1.1 \
--output-dir /tmp/validate-config
# Check for syntax errors in generated files
yamllint /tmp/validate-config/docker-compose/docker-compose.yml
tofu validate -chdir=/tmp/validate-config/tofu/Benefit: Catch configuration errors early.
# Render with SQLite
cargo run -- render --env-file envs/sqlite.json --instance-ip 10.0.0.1 --output-dir /tmp/sqlite-artifacts
# Render with MySQL
cargo run -- render --env-file envs/mysql.json --instance-ip 10.0.0.1 --output-dir /tmp/mysql-artifacts
# Compare configurations
diff -r /tmp/sqlite-artifacts/ /tmp/mysql-artifacts/Benefit: Understand configuration differences between setups.
| Aspect | render | provision |
|---|---|---|
| Purpose | Generate artifacts only | Deploy infrastructure |
| Infrastructure | None created | Creates VMs/servers |
| State Change | No change | Created → Provisioned |
| IP Address | User-provided (any IP) | From actual infrastructure |
| Output Location | User-specified directory | build/{env}/ directory |
| Cost | Free | Provider charges apply |
| Time | Seconds | Minutes (depends on provider) |
| Use Case | Preview, validation, manual deployment | Actual deployment |
Key Principle: Render generates identical artifacts to provision (except IP addresses).
If you used --env-name mode, you can continue with normal workflow:
# Review artifacts
ls -la ./preview-my-env/
# If satisfied, provision infrastructure (writes to build/my-env/)
torrust-tracker-deployer provision my-env
# Or continue manual deployment from preview directoryIf you used --env-file mode, artifacts are ready for manual deployment:
# Deploy infrastructure manually
cd <output-dir>/tofu/
tofu init && tofu apply
# Configure with Ansible
cd ../ansible/
ansible-playbook -i inventory.yml playbooks/configure.ymlProblem: Cannot find environment with specified name
Solution: Verify environment exists and is in Created state
# List environments
torrust-tracker-deployer list
# Check environment state
torrust-tracker-deployer show <env-name>
# Should show: State: CreatedProblem: Cannot read configuration file
Solution: Check file path
# Use absolute path
torrust-tracker-deployer render \
--env-file /absolute/path/to/config.json \
--instance-ip 192.168.1.100 \
--output-dir /tmp/artifacts
# Or relative path from working directory
torrust-tracker-deployer render \
--env-file ./envs/my-config.json \
--instance-ip 192.168.1.100 \
--output-dir ./artifactsProblem: IP address validation fails
Solution: Use valid IPv4 or IPv6 format
# Valid IPv4
torrust-tracker-deployer render --env-name test --instance-ip 192.168.1.100 --output-dir ./test-artifacts
# Valid IPv6
torrust-tracker-deployer render --env-name test --instance-ip 2001:db8::1 --output-dir ./test-artifacts-v6
# Invalid (will fail)
torrust-tracker-deployer render --env-name test --instance-ip invalid-ip --output-dir ./testProblem: Environment is in Provisioned state (not Created)
Behavior: Command fails with an error explaining the state constraint
❌ Environment 'my-env' is already in 'Provisioned' state.
The 'render' command only works for environments in 'Created' state.
Solution:
-
For provisioned environments, artifacts were generated during provision and are in
build/my-env/ -
To preview with different configuration, use
--env-filemode instead:torrust-tracker-deployer render --env-file envs/new-config.json --instance-ip <ip> --output-dir ./preview
Problem: The specified output directory already exists
Behavior: Command fails to prevent accidental overwrites
❌ Output directory already exists: ./preview-artifacts
Solution: Choose one:
# Option 1: Use different directory
torrust-tracker-deployer render ... --output-dir ./preview-artifacts-2
# Option 2: Overwrite with --force (use with caution)
torrust-tracker-deployer render ... --output-dir ./preview-artifacts --force
# Option 3: Remove existing directory
rm -rf ./preview-artifacts
torrust-tracker-deployer render ... --output-dir ./preview-artifactscreate- Create environment (prerequisite for--env-namemode)provision- Provision infrastructure (uses same artifact generation)validate- Validate configuration file (useful before rendering)show- Show environment state
- Manual E2E Testing: Render Verification - Step-by-step testing guide
- Template System Architecture - How templates are rendered
- Configuration Schema - Configuration file format