There is nothing quite like having friends over for a sim-racing session.
Between Gran Turismo 7 on the PlayStation 5 and the F1 games on PC, it doesn’t take long before a casual session turns into a competition over tenths of a second.
For a while we kept track of lap times manually.
That worked reasonably well, right up until somebody lost the bit of paper containing the fastest lap.
Clearly, this required a database.
So I built my own self-hosted sim-racing leaderboard.
The original idea was loosely inspired by the familiar motorsport leaderboard format: set a lap, put your name on the board and spend the rest of the evening trying to knock somebody else off the top.
The project has since evolved well beyond that original idea.
It now supports separate F1 and Gran Turismo 7 leaderboards, has its own administrator login, uses dedicated MariaDB tables, works properly on mobile devices and is published securely through Cloudflare Tunnel.
And naturally, it now has considerably more dramatic artwork than a lap-time database really needs.
🏁 The Live Leaderboard
The finished leaderboard is running publicly here:
Visitors can view the results without logging in.
Adding new lap times is restricted behind the administrator login.
The current interface has two main sections:
- F1
- Gran Turismo 7
The active game is selected using the pill-style buttons at the top of the page.
Each leaderboard currently displays the latest 10 recorded lap times, keeping the page compact while the complete result history remains stored in MariaDB.

The current RealmLabs Sim Racing leaderboard with F1 and Gran Turismo 7 tabs.
The Sim Rig
Before getting into the web application, this is the hardware producing the lap times.
Sim Hardware
- Fanatec direct-drive wheel base
- Fanatec steering wheel
- Fanatec load-cell pedals
- Dedicated sim-racing cockpit
Platforms
- PlayStation 5 — primarily Gran Turismo 7
- Gaming PC — F1
- AMD Ryzen based PC
- NVIDIA GTX 1080 Ti
The system itself isn’t tied to a specific console or PC.
The web application simply receives a player, circuit and lap time and stores the result in the appropriate database table.
The Tech Stack
I wanted the project to remain lightweight and entirely self-hosted.
The finished setup uses:
- Synology NAS — application host
- PHP — application logic
- MariaDB / MySQL — lap-time database
- HTML5 — page structure
- CSS — RealmLabs-style interface
- JavaScript — modal and interface controls
- PHP Sessions — administrator login
- Cloudflare Tunnel — secure public access
- WordPress — project documentation
There is no hosted application platform or heavyweight framework involved.
It is essentially PHP and MariaDB doing exactly what I need.
F1 and Gran Turismo 7
One of the biggest changes from the first version was splitting the leaderboard into two separate racing categories.
At the top of the interface are two tabs:
F1
and
Gran Turismo 7
Selecting a tab changes both the visible leaderboard and the behaviour of the Add New Time control.
This means I didn’t need a second application or separate web page.
The same frontend handles both games while PHP selects the appropriate backend data.
[IMAGE – F1 / GRAN TURISMO 7 TAB AREA]
The Database
The application uses the existing sim_racing MariaDB database.
The F1 side continues to use the existing leaderboard data, while Gran Turismo 7 has its own tables:
sim_racing
├── f1_leaderboards
├── gt7_entries
└── gt7_tracks
The separation makes sense because the two games handle circuits slightly differently.
Gran Turismo 7 Circuit Experience
For Gran Turismo 7 I decided against loading every possible track configuration into the database.
GT7 contains a huge number of circuit variants, reverse layouts and short configurations.
That would make the track selector unnecessarily messy.
Instead, the leaderboard is based around Circuit Experience layouts.
This gives us a much smaller and more useful list of circuits and, importantly, everybody is effectively comparing laps under the same type of challenge.
The circuits are stored in:
gt7_tracks
while submitted results are stored in:
gt7_entries
Each GT7 result references the relevant track using its database ID.
A simplified structure looks like this:
CREATE TABLE gt7_tracks (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
track_name VARCHAR(200) NOT NULL,
sort_order INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uq_gt7_track_name (track_name)
);
The results table contains the player and lap time:
CREATE TABLE gt7_entries (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
player VARCHAR(100) NOT NULL,
track_id INT UNSIGNED NOT NULL,
lap_time VARCHAR(12) NOT NULL,
lap_time_ms INT UNSIGNED NOT NULL,
date DATE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
Although the recording date remains stored in the database, I eventually removed it from the visible leaderboard.
For this application, the important information is simply:
LAP TIME | TRACK | PLAYER
Much cleaner.
Storing Lap Times Properly
The displayed lap time remains human-readable:
01:21.207
but GT7 also stores the result internally as milliseconds.
For example:
01:21.207
becomes:
81207 ms
That makes sorting lap times much more reliable than trying to order formatted text strings.
The database can simply sort using:
ORDER BY lap_time_ms ASC
which guarantees that the fastest lap comes first.
Adding a New Time
When I am logged in, the top navigation includes:
- Add New Time
The form is contextual.
If I am viewing the F1 tab, the form submits an F1 result.
If I am viewing Gran Turismo 7, it loads the track list directly from gt7_tracks and stores the result in gt7_entries.
That keeps the interface simple.
There is no additional game selector inside the popup because the tab already tells the application what kind of result is being entered.
[IMAGE – ADD NEW TIME FORM]
Public Leaderboard, Private Controls
Once the application became publicly accessible, I didn’t want every visitor to have access to the lap-entry form.
So the leaderboard has a simple PHP administrator login.
Public users can:
- View the F1 leaderboard
- View the Gran Turismo 7 leaderboard
- View lap times
- View tracks
- View players
They cannot:
- Add results
- Open the lap-entry interface
- Access administrator controls
Once authenticated, the administrator gets:
-
- Add New Time
- Logout
The authentication is deliberately simple because this is a small private administration interface rather than a multi-user public service.
The important part is that access isn’t only hidden in the frontend.
The PHP submission handler also checks the active session before accepting a new result.
Hiding a button isn’t security.
Making It Look Like RealmLabs
The functional side of the project came first.
Then, as usual, I spent far too much time making it look better.
The interface now uses the same general design language as RealmLabs:
- Dark background
- Rounded content panels
- Red accent colours
- Pill-style buttons
- Responsive layouts
- Compact leaderboard tables
- Modal entry form
I also created custom racing artwork for the application.
The current hero banner uses a Red Bull-inspired Formula car on the F1 side and a GT-R R35 on the Gran Turismo side, giving the two sections their own visual identity while keeping the application as one design.
[IMAGE – FULL-WIDTH REALMLABS SIM RACING HERO BANNER]
At the bottom of the page is an equally unnecessary but entirely appropriate footer:
Yet another genius concoction from Mr. Huckle
[IMAGE – MR HUCKLE FOOTER BANNER]
Subtlety was never really the objective.
Making It Mobile Friendly
The original desktop interface worked well, but tables and wide racing banners are particularly good at exposing bad responsive design.
The mobile version therefore received its own pass.
Rather than rebuilding the desktop layout, I used responsive CSS rules that only take effect on smaller screens.
On mobile:
- Navigation pills wrap cleanly
- The hero banner scales to the available width
- The leaderboard uses the full screen width
- The date column has been removed
- Tracks can wrap onto another line where necessary
- The entry form adapts to narrow screens
- The footer remains readable
The desktop design remains effectively unchanged.
[IMAGE – MOBILE LEADERBOARD SCREENSHOT]
The finished table is deliberately simple:
LAP TIME TRACK PLAYER
01:08.073 Austrian GP Rob
01:19.853 Australian GP Paul
01:21.207 Italian GP Paul
No unnecessary columns and, importantly, no horizontal swiping just to see who set the lap.
Publishing It Through Cloudflare
Internally, the application still runs from my Synology NAS.
Rather than forwarding a web port directly through the router, I publish it using Cloudflare Tunnel.
The public address is:
The basic path is:
Internet
│
▼
leaderboards.realmlabs.uk
│
▼
Cloudflare
│
▼
Cloudflare Tunnel
│
▼
Home Network
│
▼
Synology
│
▼
PHP / MariaDB
That gives the application a normal HTTPS address without directly exposing the web server to the internet.
The Finished Architecture
What started as somewhere to store a few lap times now looks roughly like this:
SIM RACING
│
├───────────────┐
│ │
▼ ▼
F1 GT7
│ │
▼ ▼
F1 TABLE GT7 ENTRIES
│
▼
GT7 TRACKS
│ │
└───────┬───────┘
│
▼
PHP APPLICATION
│
▼
ADMIN LOGIN
│
▼
LIVE LEADERBOARD
│
┌─────┴─────┐
│ │
▼ ▼
LOCAL ACCESS CLOUDFLARE
│
▼
leaderboards.realmlabs.uk
And underneath all of that is still a relatively small PHP application.
No enormous framework.
No cloud database subscription.
No unnecessary infrastructure.
Just a Synology, PHP, MariaDB and Cloudflare Tunnel.
What I Like About This Project
This is exactly the kind of thing I like using the homelab for.
The original requirement was:
We need somewhere to keep our lap times.
A spreadsheet would have solved it.
Instead, naturally, I ended up with:
- A MariaDB database
- Separate F1 and GT7 data
- A PHP application
- Administrator authentication
- A responsive custom frontend
- Dedicated racing artwork
- A public subdomain
- A Cloudflare Tunnel
Completely reasonable.
But that is also the point.
A homelab gives you somewhere to turn small ideas into proper working systems without needing to buy another hosted service or force an off-the-shelf application to do something it wasn’t designed for.
The Verdict
The leaderboard started as a very small project designed to replace bits of paper containing lap times.
It now supports both F1 and Gran Turismo 7, uses a proper MariaDB backend, dynamically loads GT7 Circuit Experience tracks, protects result entry behind an administrator login, works on desktop and mobile, and is securely published through Cloudflare Tunnel.
And the live version remains available here:
Set a lap.
Add the result.
See your name on the board.
Then spend the next hour trying to work out where the missing two tenths went.
The database remembers.

