Deploy a Torrust Tracker to Hetzner Cloud using Docker in minutes.
Docker only supports cloud providers (Hetzner). For LXD local development, see Native Installation.
- Docker installed and running
- SSH key pair for VM access (e.g.,
~/.ssh/id_ed25519) - Hetzner Cloud account with API token (create one)
docker pull torrust/tracker-deployer:latestmkdir -p data build envs
chmod 700 envs # Contains sensitive configurationdocker run --rm \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
torrust/tracker-deployer:latest \
create template --provider hetzner envs/my-hetzner-env.jsonOpen envs/my-hetzner-env.json and update:
{
"environment": {
"name": "my-hetzner-env"
},
"ssh_credentials": {
"private_key_path": "/home/deployer/.ssh/id_ed25519",
"public_key_path": "/home/deployer/.ssh/id_ed25519.pub",
"username": "torrust",
"port": 22
},
"provider": {
"provider": "hetzner",
"api_token": "YOUR_HETZNER_API_TOKEN_HERE",
"server_type": "cx22",
"location": "nbg1",
"image": "ubuntu-24.04"
},
"tracker": {
"http_api": {
"bind_address": "0.0.0.0:1212",
"admin_token": "YOUR_SECURE_ADMIN_TOKEN"
}
},
"health_check_api": {
"bind_address": "127.0.0.1:1313"
},
"grafana": {
"admin_user": "admin",
"admin_password": "YOUR_SECURE_GRAFANA_PASSWORD"
}
}Important: The
private_key_pathandpublic_key_pathare paths inside the container. When you mount~/.ssh:/home/deployer/.ssh:ro, your host keys become available at/home/deployer/.ssh/inside the container.Note on displayed paths: Commands like
showdisplay paths as they exist inside the container (e.g.,/home/deployer/.ssh/torrust_tracker_rsa). These are not host paths. To SSH from your host machine, use the corresponding host path (e.g.,~/.ssh/torrust_tracker_rsa).
For publicly exposed servers, never use default passwords. Generate secure random credentials:
# Generate a 32-character token for tracker HTTP API admin_token
openssl rand -base64 32
# Generate a 24-character password for Grafana admin
openssl rand -base64 18Store these passwords securely (password manager) - you'll need them to access the services.
| Field | Description | Example |
|---|---|---|
environment.name |
Unique environment identifier | my-hetzner-env |
ssh_credentials.private_key_path |
Container path to SSH private key | /home/deployer/.ssh/id_ed25519 |
ssh_credentials.public_key_path |
Container path to SSH public key | /home/deployer/.ssh/id_ed25519.pub |
provider.api_token |
Hetzner API token | hcloud_xxx... |
provider.server_type |
Server size | cx22, cx32, cx42 |
provider.location |
Datacenter | nbg1, fsn1, hel1 |
tracker.http_api.admin_token |
Tracker API authentication token | (generated secure token) |
tracker.health_check_api.bind |
Health check binding (use 127.0.0.1 for security) |
127.0.0.1:1313 |
grafana.admin_user |
Grafana admin username | admin |
grafana.admin_password |
Grafana admin password | (generated secure password) |
prometheus.scrape_interval_in_secs |
Metrics scrape interval | 15 |
See Hetzner Provider Guide for all options.
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
create environment --env-file /var/lib/torrust/deployer/envs/my-hetzner-env.jsonCreate the Hetzner Cloud server:
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
provision my-hetzner-envDuration: ~60-90 seconds
Install Docker and dependencies on the server:
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
configure my-hetzner-envDuration: ~60-90 seconds
Pull the tracker Docker image on the server:
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
release my-hetzner-envDuration: ~10-15 seconds
Start the tracker service:
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
run my-hetzner-envDuration: ~10-15 seconds
Check your tracker is running:
# Show environment details (includes server IP)
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
torrust/tracker-deployer:latest \
show my-hetzner-env
# Run built-in health tests
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
test my-hetzner-envAfter deployment, your services are available at (replace <IP> with your server's IP):
| Service | URL | Notes |
|---|---|---|
| HTTP Tracker | http://<IP>:7070/announce |
BitTorrent HTTP tracker announce |
| UDP Tracker | udp://<IP>:6969/announce |
BitTorrent UDP tracker announce |
| Tracker API | http://<IP>:1212/api |
Requires admin_token for auth |
| Health Check | http://<IP>:1313/health |
Bound to localhost only (internal) |
| Grafana | http://<IP>:3100 |
Metrics dashboard (admin credentials) |
| Prometheus | http://localhost:9090 |
Internal only - not exposed externally |
Note: The health check endpoint (
1313) is bound to127.0.0.1by default for security. Access it via SSH if needed.
# Get tracker stats (requires admin token)
curl "http://<IP>:1212/api/v1/stats?token=YOUR_ADMIN_TOKEN"
# Test HTTP tracker (should return 200)
curl -s -o /dev/null -w "%{http_code}" "http://<IP>:7070/announce"When you're done, destroy the environment to stop billing:
docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest \
destroy my-hetzner-env
⚠️ Important: Remember to destroy Hetzner resources when not in use to avoid charges.
Add this to your ~/.bashrc or ~/.zshrc for convenience:
alias deployer='docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest'Then use:
deployer create template --provider hetzner envs/my-env.json
deployer provision my-env
deployer configure my-env
deployer release my-env
deployer run my-env
deployer destroy my-env# Ensure correct permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519- Verify token in Hetzner Console
- Ensure token has Read & Write permissions
- Check token is correctly copied (no extra spaces)
Ensure you're mounting the data directory consistently:
ls -la ./data/ # Should show your environmentIf provisioning fails, the environment enters a ProvisionFailed state. You cannot re-provision directly - you must destroy and recreate:
# Destroy the failed environment
deployer destroy my-hetzner-env
# Recreate from scratch
deployer create environment --env-file /var/lib/torrust/deployer/envs/my-hetzner-env.json
deployer provision my-hetzner-envTip: Check
data/<env-name>/environment.jsonto see the current state and error details.
When running show or other commands, paths displayed (like SSH key paths) are container paths, not host paths:
# Output from 'show' command shows:
# SSH Key: /home/deployer/.ssh/torrust_tracker_rsa
#
# This is the path INSIDE the container.
# On your host, this maps to wherever you mounted from:
# ~/.ssh/torrust_tracker_rsa (if you used -v ~/.ssh:/home/deployer/.ssh:ro)To SSH from your host machine, translate the container path to your host path:
# Container shows: ssh -i /home/deployer/.ssh/torrust_tracker_rsa torrust@<IP>
# On your host use: ssh -i ~/.ssh/torrust_tracker_rsa torrust@<IP>This is expected. Docker only supports cloud providers. For LXD, use Native Installation.
- Hetzner Provider Guide - Server types, locations, pricing
- Docker Image Reference - Advanced Docker usage
- Command Reference - All available commands
For automation, here's the full workflow:
#!/bin/bash
set -e
ENV_NAME="my-hetzner-env"
# Setup
mkdir -p data build envs
chmod 700 envs
# Common docker run prefix
DEPLOYER="docker run --rm \
-v $(pwd)/data:/var/lib/torrust/deployer/data \
-v $(pwd)/build:/var/lib/torrust/deployer/build \
-v $(pwd)/envs:/var/lib/torrust/deployer/envs \
-v ~/.ssh:/home/deployer/.ssh:ro \
torrust/tracker-deployer:latest"
# Generate template (edit afterwards with your API token)
$DEPLOYER create template --provider hetzner envs/${ENV_NAME}.json
echo "Edit envs/${ENV_NAME}.json with your Hetzner API token, then press Enter"
read
# Deploy
$DEPLOYER create environment --env-file /var/lib/torrust/deployer/envs/${ENV_NAME}.json
$DEPLOYER provision ${ENV_NAME}
$DEPLOYER configure ${ENV_NAME}
$DEPLOYER release ${ENV_NAME}
$DEPLOYER run ${ENV_NAME}
echo "Deployment complete! Run: $DEPLOYER show ${ENV_NAME}"