This guide covers running the Nexus tracker using Docker.
The easiest way to run the tracker is using the official pre-built image from GitHub Container Registry.
# Pull the latest image
docker pull ghcr.io/zquestz/nexus-trackerd:latest
# Run the container (open tracker, no WebSocket)
docker run -d \
-p 7510:7510 \
-v nexus-tracker-data:/home/nexus-tracker/.local/share/nexus-trackerd \
--name nexus-trackerd \
ghcr.io/zquestz/nexus-trackerd:latest
# With WebSocket support enabled
docker run -d \
-p 7510:7510 \
-p 7511:7511 \
-e NEXUS_TRACKER_WEBSOCKET=true \
-v nexus-tracker-data:/home/nexus-tracker/.local/share/nexus-trackerd \
--name nexus-trackerd \
ghcr.io/zquestz/nexus-trackerd:latestThe repository ships a docker-compose.yml that defines two services: the BBS server (nexusd) and the tracker (nexus-trackerd). Running docker compose up -d from the repo root brings up both services by default.
If you only want the tracker, scope the command to the service:
# Start the tracker only (skips the server)
docker compose up -d nexus-trackerdTo enable WebSocket support, edit docker-compose.yml and add the WebSocket port and env var to the nexus-trackerd service:
ports:
- "7511:7511"
environment:
- NEXUS_TRACKER_WEBSOCKET=true
- NEXUS_TRACKER_WEBSOCKET_PORT=7511| Tag | Description |
|---|---|
latest |
Most recent stable release |
0.1.5 |
Specific version |
0.1 |
Latest patch release in 0.1.x series |
0 |
Latest release in 0.x.x series |
Pre-built images support both architectures in a single manifest:
linux/amd64(x86_64)linux/arm64(aarch64)
Docker automatically pulls the correct architecture for your system.
If you prefer to build the image yourself, you can use the included Dockerfile.tracker.
The repo's docker-compose.yml defines both nexusd and nexus-trackerd. The commands below scope to nexus-trackerd so only the tracker is built and run.
To build from source, uncomment the build: block under nexus-trackerd in docker-compose.yml, then:
# Clone the repository
git clone https://github.com/zquestz/nexus.git
cd nexus
# Start the tracker (builds automatically)
docker compose up -d nexus-trackerd
# View logs
docker compose logs -f nexus-trackerd
# Stop the tracker
docker compose stop nexus-trackerd# Build the image
docker build -f Dockerfile.tracker -t nexus-trackerd .
# Run the container
docker run -d \
-p 7510:7510 \
-v nexus-tracker-data:/home/nexus-tracker/.local/share/nexus-trackerd \
--name nexus-trackerd \
nexus-trackerd| Variable | Default | Description |
|---|---|---|
NEXUS_TRACKER_BIND |
0.0.0.0 |
IP address to bind to |
NEXUS_TRACKER_PORT |
7510 |
Tracker port |
NEXUS_TRACKER_WEBSOCKET |
(empty) | Set to any value to enable WebSocket support |
NEXUS_TRACKER_WEBSOCKET_PORT |
7511 |
WebSocket tracker port (requires NEXUS_TRACKER_WEBSOCKET) |
NEXUS_TRACKER_LOG_LEVEL |
info |
Log level (none, error, warn, info, debug) |
NEXUS_TRACKER_LOG_RETENTION |
30d |
Log file retention (e.g. "30d", "7d", "0" for stderr only) |
NEXUS_TRACKER_NO_LOG_TIMESTAMPS |
true |
Disable stderr timestamps (Docker provides its own); set to empty to re-enable |
NO_COLOR |
(empty) | Set to any value to force plain stderr output |
NEXUS_TRACKER_MAX_ENTRIES |
10000 |
Maximum number of registered servers (0 = unlimited; list responses remain frame-capped) |
NEXUS_TRACKER_MAX_ENTRIES_PER_IP |
1 |
Maximum entries from one IPv4 address or IPv6 /64 (0 = unlimited) |
NEXUS_TRACKER_REFRESH_INTERVAL |
300 |
Refresh interval in seconds (range 120–600) |
NEXUS_TRACKER_RATE_CONNECTIONS |
20 |
Connections per minute per IPv4 address or IPv6 /64 (0 = unlimited) |
NEXUS_TRACKER_RATE_AUTH_FAILURES |
5 |
Failed authentication attempts per minute per IPv4 address or IPv6 /64 (0 = unlimited) |
environment:
- NEXUS_TRACKER_LOG_LEVEL=debugports:
- "7510:7510"
- "7511:7511"
environment:
- NEXUS_TRACKER_WEBSOCKET=trueenvironment:
- NEXUS_TRACKER_BIND=::environment:
- NEXUS_TRACKER_MAX_ENTRIES=1000
- NEXUS_TRACKER_MAX_ENTRIES_PER_IP=3UPnP is not wired up in the container image — port forwarding is the host's job in containerized deployments. Use the host's networking stack or your orchestrator's port-publish mechanism instead.
The container does not start with any passwords set; both flows are open by default. To set a password, run the binary's subcommand inside the running container:
# Set the registration password (interactive prompt)
docker exec -it nexus-trackerd nexus-trackerd set-password registration
# Set the listing password
docker exec -it nexus-trackerd nexus-trackerd set-password listing
# Clear a password (open that flow back up)
docker exec -it nexus-trackerd nexus-trackerd clear-password registrationThe hash files live in the volume (registration.hash / listing.hash), so they survive container restarts. After setting or clearing a password, send SIGHUP to the running tracker so it picks up the change without restarting:
docker kill -s HUP nexus-trackerdSee Password Management for the full lifecycle and rotation flow.
The named volume nexus-tracker-data stores:
- TLS certificate (
tracker.crt,tracker.key) - Password hashes (
registration.hash,listing.hash) when set - Log files (
logs/)
The server registry is not persisted — it's held in memory and rebuilds via refresh after a restart.
Data persists across container restarts and rebuilds.
Mount a host directory instead of a named volume:
volumes:
- /path/on/host:/home/nexus-tracker/.local/share/nexus-trackerdports:
- "7510:7510" # Tracker (TCP)
# Uncomment for WebSocket support (requires NEXUS_TRACKER_WEBSOCKET=true)
# - "7511:7511"To use a different external port:
ports:
- "8510:7510" # External 8510 → Internal 7510Bind to a specific host interface:
ports:
- "192.168.1.100:7510:7510"docker build -f Dockerfile.tracker -t nexus-trackerd .git pull
docker compose build --no-cache nexus-trackerd
docker compose up -d nexus-trackerd# Follow tracker logs
docker compose logs -f nexus-trackerd
# Last 100 lines
docker compose logs --tail 100 nexus-trackerd
# Specific container
docker logs nexus-trackerddocker compose restart nexus-trackerddocker compose stop nexus-trackerdThe default docker-compose.yml defines both nexusd and nexus-trackerd. Tear down the whole stack and delete all volumes:
docker compose down -vWarning: This deletes all data for both services — server users/settings/files and tracker passwords/certs.
To remove just the tracker's data, stop the service and remove only its named volume:
docker compose stop nexus-trackerd
docker compose rm -f nexus-trackerd
docker volume rm nexus_nexus-tracker-data# Pull the latest tracker image
docker pull ghcr.io/zquestz/nexus-trackerd:latest
# Restart the tracker with the new image
docker compose stop nexus-trackerd
docker compose up -d nexus-trackerdgit pull
docker compose build --no-cache nexus-trackerd
docker compose up -d nexus-trackerdThe tracker's data lives in the nexus-tracker-data named volume (resolved by Docker as nexus_nexus-tracker-data when the project name is nexus). The server has its own volume and is unaffected by these commands.
# Stop the tracker (server keeps running)
docker compose stop nexus-trackerd
# Backup the volume
docker run --rm \
-v nexus_nexus-tracker-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/nexus-tracker-backup.tar.gz -C /data .
# Restart the tracker
docker compose up -d nexus-trackerd# Stop the tracker (server keeps running)
docker compose stop nexus-trackerd
# Restore the volume
docker run --rm \
-v nexus_nexus-tracker-data:/data \
-v $(pwd):/backup \
alpine sh -c "rm -rf /data/* && tar xzf /backup/nexus-tracker-backup.tar.gz -C /data"
# Restart the tracker
docker compose up -d nexus-trackerdNote: the registry itself isn't in the backup (it's in-memory). After a restore, you only get the certificate, password hashes, and old logs back. Registered servers re-register on their next refresh.
The default restart: unless-stopped ensures the tracker restarts after crashes.
Add resource constraints:
services:
nexus-trackerd:
# ... other settings ...
deploy:
resources:
limits:
cpus: "1"
memory: 256MThe tracker is far less resource-hungry than a BBS server — it holds an in-memory registry capped by --max-entries and serves short request/response exchanges. 256MB of memory is generous for the default 10,000-entry cap and the 32 MiB tracker-list frame cap.
The image ships with a built-in health check that probes the tracker port. To override:
services:
nexus-trackerd:
# ... other settings ...
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "7510"]
interval: 30s
timeout: 5s
retries: 3When running behind a reverse proxy (nginx, Traefik, etc.), note that the tracker uses raw TLS connections, not HTTP. Standard HTTP reverse proxies won't work — you need TCP/TLS passthrough. Same constraint as the BBS server.
Check the tracker's logs:
docker compose logs nexus-trackerdCommon issues:
- Port already in use — change the external port
- Permission denied — check volume permissions
- Verify the container is running:
docker compose ps nexus-trackerd - Check the ports are mapped:
docker port nexus-trackerd - Verify firewall allows the ports
- Check the tracker logs for errors
Ensure you're using a volume:
docker volume ls | grep nexus-trackerIf the volume doesn't exist, data is lost when the container stops.
If you get exec format errors, Docker pulled the wrong architecture. Force the correct one:
docker pull --platform linux/amd64 ghcr.io/zquestz/nexus-trackerd:latest
# or
docker pull --platform linux/arm64 ghcr.io/zquestz/nexus-trackerd:latest- Password Management — Setting and rotating passwords
- Troubleshooting — Common issues and solutions