Setting up Rally Hero for local competition stages
Rally Hero is a relatively lightweight event management framework that I picked up about three years ago after the previous system our regional organizing committee used became unmaintainable. The core idea is straightforward: it tracks stage times, processes penalties, and outputs leaderboard JSON that downstream systems can consume. Nothing revolutionary, but it works when you actually need it to. Download the latest release from the official repository and extract it to your deployment directory. The package includes a Go binary called rally-hero, a sample configuration file, and some shell scripts for common operations. Run the binary directly - no installation wizard, no package manager required. The default config listens on port 8080 and expects PostgreSQL on localhost. I found the documentation lacks examples for staging multiple events simultaneously. Here is what I learned the hard way.
Configuration basics
The main config file is YAML. You define stages as a list of objects with name, start_time, and course_path properties. Each stage needs its own timing database entry if you want proper concurrency. The default single-database setup works fine for one event, but things get weird when you try to run two stages at overlapping times. Here is a typical stage definition:
stages:
- name: "special_stage_01"
start_time: "2024-03-15T08:00:00Z"
course_path: "/data/courses/ss01.csv"
timing_db: "rallyhero_ss01"
The timing_db property is optional but highly recommended. Without it, all stages write to the default database and you will get race conditions during data ingestion. I spent six hours debugging what looked like corrupted timing data before realizing two stages were hitting the same table simultaneously. Start the service with something like: The default port conflicts with other services running on many host machines. I changed it to 9090 on our main server to avoid breaking the timing display application that another department runs on 8080. If you skip the --config flag, Rally Hero falls back to config.yaml in the current directory. That is convenient for testing but dangerous if you forget about it.
Get the Full Details

The server logs to stdout by default. Redirect output somewhere sensible if you plan to run it unattended. Our systemd unit captures logs to /var/log/rallyhero.log with standard rotation.
Processing stage data
Feeding timing data into Rally Hero happens through a simple HTTP API. POST to /api/stages/{stage_id}/times with a JSON payload containing driver_name, vehicle_class, elapsed_time, and any penalty fields your series requires. The API rejects payloads missing required fields with a 400 error. No warning, just the error code in the response body. I encountered an edge case where drivers submitted times with milliseconds precision while the database column was defined as integer seconds. Rally Hero silently truncates the fraction and you end up with leaderboard entries that look correct but are actually wrong by half a second. This caused a dispute at our last event that took twenty minutes to resolve. The workaround is running a schema validation script before uploading any timing data:
./validate_schema.py --input times.json --schema standard_v2.json
That script checks precision, field types, and format consistency. It usually catches issues before they reach the database, cutting post-event dispute time from about 30 minutes down to roughly 5 minutes, depending on how many drivers have malformed entries. Rally Hero outputs results as JSON by default. You can also request CSV through the /export endpoint with a format parameter. The JSON structure nests stages under a results object, with each stage containing driver entries sorted by elapsed_time. There is no built-in ranking logic for different vehicle classes. If your series requires class winners, you will need to post-process the output. I wrote a Python script that groups results by vehicle_class and recalculates winners. It takes about 200 lines of code, but it runs in under a second regardless of how many drivers participated. The alternative is manually sorting through JSON in your editor, which gets tedious after a while.

Common pitfalls and limitations
The biggest limitation I have encountered is the lack of offline mode. Rally Hero requires database connectivity at all times. If your network drops during an event, the server returns 503 errors and timing data gets lost. I have seen this happen twice, both times costing us about 45 minutes of race progress. The workaround is running a local proxy that queues requests during network outages and flushes them when connectivity returns. It is not perfect but it prevents data loss. Another issue is memory usage during large events. Rally Hero loads all stage data into memory before generating exports. I ran an event with over 200 drivers across 15 stages and the process peaked at about 1.2 GB of RAM. That is manageable on most modern servers but could be problematic if you are running on constrained hardware. The documentation does not mention resource limits explicitly. If your series has more than 300 drivers per stage, expect slower response times during peak data ingestion. I had to increase the PostgreSQL connection pool size from 10 to 25 connections to handle our largest events without timeouts. The default configuration assumes smaller competitions.
Running Rally Hero with Docker
Using Docker simplifies deployment but introduces its own quirks. The official image is available but somewhat outdated. I maintain a fork with a patched config loader that respects environment variables. Here is a minimal docker-compose setup: The volume mounts preserve data across container restarts. Without them, a simple container recreation wipes all timing data. I learned this after accidentally running docker-compose down during a live event. The recovery involved restoring from a backup that was 4 hours old. The /health endpoint returns basic status information including database connectivity and active stage counts. Check this regularly during events to catch issues early. I wrote a simple monitoring script that pings /health every 30 seconds and sends alerts if the response time exceeds 500 milliseconds or if the endpoint becomes unreachable. It has prevented several missed-disaster situations by catching database connection exhaustion before drivers started complaining.
For deeper debugging, enable verbose logging with the --log-level flag. I typically use debug level during setup and warn level during actual events. The extra output helps identify configuration issues but can fill disk space quickly if left running for extended periods. Our log rotation policy keeps files under 100 MB each and archives everything older than 7 days. The API response format changed between version 2.3 and 2.4 without a migration path. Drivers who upgraded to 2.4 and then tried to use 2.3 tooling found their scripts broken. The community provided a compatibility layer but it requires manual activation. I recommend staying on a single major version unless you have tested thoroughly.

Integration with other tools
Rally Hero does not provide built-in integrations with popular timing systems like RallyStats or MyLaps. I have written custom connectors for both but they require about 150 lines of code each and depend on the respective API specifications changing. The MyLaps integration is more stable because their API is well-documented and rarely changes. RallyStats requires periodic updates when they modify their export format. If you need real-time leaderboard displays, you will need to build something yourself or find a community project. The JSON export endpoint can feed most web-based displays, but refreshing every few seconds requires client-side polling or WebSocket support that Rally Hero does not provide. Our team built a simple Flask application that polls every 10 seconds and updates a browser-based leaderboard using Server-Sent Events. It works reliably for events up to 100 simultaneous viewers.
Best practices
Always validate timing data before ingesting it. The schema validation script I mentioned earlier usually catches about 80 percent of common errors, including the millisecond precision issue and missing driver identifiers. Running it as part of your pre-event checklist saves time during competitions. Keep database connections explicit. Do not rely on the default single-database setup if you plan to run multiple stages concurrently. Each stage should have its own timing_db entry with clear naming conventions. Our convention is rallyhero_{event}_{stage_number}, which makes it obvious which database belongs to which stage when troubleshooting. Test your configuration before events. I run a dry execution with sample data about 48 hours before each competition. This catches configuration errors and resource issues that might otherwise surface during live events. The test takes about 15 minutes and has prevented several potential disasters at our club races.
Backup your database regularly. We run incremental backups every hour during events and full backups daily during event seasons. The restore procedure takes about 10 minutes and has saved us once when a faulty update corrupted our main database. Without backups, that incident would have required rebuilding the entire event schedule from paper records.

When Rally Hero is not the right choice
For small local runs with under 50 drivers, simpler tools like Excel spreadsheets or dedicated mobile apps may be more appropriate. Rally Hero shines when you need structured data processing and API access for integration with other systems. The overhead of deployment and configuration is not worth it for occasional casual events. If you require real-time synchronization between multiple timing stations, consider a purpose-built solution. Rally Hero handles concurrent data ingestion reasonably well but is not designed for distributed timing systems. We attempted this setup once and encountered race conditions that took two weeks to resolve. The alternative is using multiple databases with a consolidation step after the event, which is less elegant but more reliable. For series requiring complex penalty calculations and historical data analysis, you may need to extend Rally Hero or combine it with other tools. The built-in penalty system supports basic time additions and disqualifications but lacks support for grid drops, cumulative penalties, and multi-stage adjustments that some series require. Our workaround is post-processing results through a custom Python script that applies series-specific rules after the event concludes.