· Alexander · Monitoring · 27 min read
Meet Tracearr: Interview with the Developer - Part 2
We sit down with Connor Gallopo, the creator of Tracearr, to discuss the inspiration, challenges, and future of unified media server monitoring.

In Part 1, we took an introductory look at Tracearr and why it’s becoming a must-have for media server enthusiasts. Now, we’re diving deeper. I had the pleasure of sitting down with Connor Gallopo, the lead developer behind Tracearr, to talk about how the project came to be and where it’s headed.
What drew you to building Tracearr? Was the gap between Tautulli (Plex only) and Jellystat (Jellyfin/Emby only) the primary frustration, or was there a specific moment that made you say “I need to build this myself”?
In the beginning Tracearr was something I built for myself. I’m a long time Plex user with a big library that I share with friends and family, so metric collection has always mattered to me, and for a long time Tautulli filled that need. But after crossing 50 users and many hundreds of terabytes of media a couple of problems started to show up. The first was performance. At that scale SQLite really started to become the bottleneck and some pages were taking 10-20 seconds to load. The second was that the stuff I actually wanted, deep performance metrics and real tooling for detecting account sharing, just didn’t exist.
I did look at contributing some of this back upstream instead of building something new, and I even put up a PR. That experience is honestly what tipped me over. The reception wasn’t what I was hoping for, and between that and the fact that the underlying stack was never going to get me the performance I was after, I realized the thing I wanted just wasn’t going to happen there. So I built a first version of Tracearr locally, proved out the core concepts, and decided to take a stab at turning it into a real open source project.
The multi media server piece was part of the plan from the very start. My thinking was pretty simple. If I’m going to put another monitoring tool out into the world, I’m going to tackle the one thing nobody else has done and support all three of the big media servers at once. Tautulli does Plex, Jellystat does Jellyfin and Emby, but nothing does all of them together. If you’re going to do it, do it right, and do it so the most people possible can actually use it.
Tracearr went from initial scaffold to around 2,000 stars and 250+ releases in roughly six months. That’s a remarkable rate of development. Were you surprised by the response from the community, or did you sense there was demand before you launched?
I was absolutely blown away, and honestly I still am. I knew there was demand for some of what I was building, like support for Jellyfin, Emby, and Plex, or even multiple of each at the same time, but I also figured the rules system wasn’t going to be for everyone.
One of the first moments I realized Tracearr might grow quickly was back in the pre-release phase, when I was working on the Unraid community app. It ended up going live earlier than I expected, and people started organically finding their way to the Discord, which at that point was honestly just a placeholder I had thrown up. Overnight about 30 people joined, before I had made any kind of formal Reddit announcement, and I really was not expecting to see that kind of activity. I had to scramble. I think we hit around 150 people in the Discord before any real announcement ever went out, and that was the writing on the wall for me that there was going to be some real growth coming.
Tracearr’s backend is built on PostgreSQL with TimescaleDB for time-series data. Why did you choose that stack, and how does it help you handle the unique challenges of media monitoring data, which includes both high-cardinality metadata and time-series session data?
Honestly a big part of it is that I work with TimescaleDB and I know how well it handles time-series data. I’ve seen it do some pretty crazy things at scale, so I was already confident in it, and I liked the idea of using it on an open source project to put it in front of more people who might not have run into it otherwise.
The practical reason though is that it is just Postgres with an extension. Session data is time-series, but pretty much everything around it is relational. Users, servers, rules, violations, the libraries, all of it is just regular relational data, foreign keys and all. If I went with a purpose built time-series database I would have ended up running two databases in one self hosted app, and other than Redis for caching and queues, that is just absurd to me. Two stateful databases is just a maintenance headache. More risk, more overhead, and one more thing every self hoster has to back up and babysit. Timescale lets me get the time-series wins like automatic partitioning, compression, and continuous aggregates for the hot data, while everything else stays as regular Postgres.
As for handling both the high-cardinality metadata and the time-series side, the sessions table is the interesting one. Plex, Jellyfin, and Emby each hand you a ton of metadata per session, video codec, resolution, transcode settings, audio channels, subtitle info, you name it. Instead of dumping everything into JSONB or flattening all of it into columns, I split it based on how I actually query it. The stuff I filter and aggregate on constantly, things like the video and audio codecs, resolution, device, IP, and the geo fields, those are real indexed columns. The deeper structural detail you only ever look at when you open up a single session, like the full video and transcode breakdown, lives in JSONB. So the queries that run all the time stay fast, and I still get the flexibility I need for three media servers that all change their APIs whenever they feel like it.
TimescaleDB uses hypertables to automatically partition time-series data into chunks, keeping queries fast as history grows. With session data, you’re dealing with irregular write bursts (everyone starts watching at 8pm) and range-heavy reads (give me all streams for this user over the last 90 days). How are you structuring your hypertables and chunk intervals to handle that pattern, and did you consider a purpose-built TSDB like InfluxDB before landing on TimescaleDB’s PostgreSQL extension approach?
There are two hypertables, and I gave them different chunk intervals on purpose because they get used completely differently.
Sessions use a 7-day chunk interval on the start time. I tuned that around two competing things. The sharing detection rules are always looking at recent data, usually somewhere in the last 24 hours to 7 days for stuff like impossible travel, so I want that window staying hot and uncompressed. Older data I want compressed to claw back space. Compression kicks in at 7 days and I segment it by user and server, because the same user’s sessions tend to compress really well once they are grouped together like that.
The bursty-write, range-read pattern is really handled by the indexes. On top of the normal composite indexes you would expect, I lean on a bunch of partial indexes that are each tuned for one specific query. There is one that only covers rows that actually have geo data, which powers the stream map. One that only covers actively playing streams, which keeps the live dashboard query tiny. Separate ones for transcodes, music, and live TV, each ignoring the 90 plus percent of rows that do not match. There is also a covering index for the top-content stats so that aggregate never has to touch the table. So when the 8pm rush hits and everybody starts a stream at once, all those writes just pile onto the current chunk, and the indexes stay small because they are partitioned off. The read side works out too. If someone pulls the last 90 days for a user, it only ends up touching a handful of chunks and the few indexes it actually needs.
On top of that, the continuous aggregates do a lot of the heavy lifting. Things like per-user daily watch engagement and daily bandwidth get rolled up on a schedule, so the expensive analytics queries read pre-aggregated data instead of scanning raw sessions every time.
The second hypertable is library snapshots, and it gets a 1-day chunk interval because it is written once a day per library per server, a totally different pattern. That one compresses after 3 days and has a 90-day retention policy, since the aggregates already keep the long-term summary. Sessions, on the other hand, have no retention at all. Raw stream history sticks around forever and just gets compressed as it ages.
On InfluxDB, I will be straight with you, I did not evaluate it for Tracearr. I have looked at it for other things in the past, but for this it never really got off the ground, and it comes back to the same thing. I was not about to run a second database next to Postgres in a self hosted app. The whole point was to keep this to one database that does both jobs, and Timescale already does the time-series side well enough that I never felt like I was giving anything up.
What challenges did you face when you started integrating with multiple media servers (Plex, Jellyfin, & Emby) and how did you design Tracearr’s architecture to be flexible enough to support them all without becoming a maintenance nightmare?
Throughout the design and development of Tracearr I ran into plenty of challenges here. For starters, it is uncharted territory. There are plenty of great tools out there like Tautulli and Jellystat that do Plex well, or Jellyfin and Emby well, but nothing does all three.
So in the initial discovery phase the whole goal was to find the shared concepts. The key models were users, sessions, libraries, and library items, and those concepts do not really drift much from one application to the next. That let me abstract all of that, and more, into what I call the IMediaServerClient interface. It defines the core behaviors every server has, like fetching sessions, users, and libraries, testing a connection, or terminating a stream, even if the actual implementation behind each one is completely different. There is a factory that takes a server’s config and hands back the right implementation, so everything else in the app, the poller, the sync jobs, the admin UI, just calls the same methods and never has to care which kind of server it is talking to.
The one real judgment call was how to split up the implementations. Jellyfin and Emby share a base class, because Jellyfin is a fork of Emby’s last open-source release and their APIs are nearly identical, so the parsing is basically the same code for both. Plex gets its own completely independent implementation. It is a decades-old API with its own auth, its own pagination, native server side events, and even some endpoints that still hand back XML instead of JSON. Trying to force Plex into a shared base with the other two would have been the actual maintenance nightmare. So Plex stays off on its own, and the other two share what they actually share. That is the only way it stays manageable.
Honestly the biggest challenge was not the initial integration, it was keeping behavioral parity across three servers that drift apart in subtle ways. Jellyfin and Emby are the perfect example. They are so close that the parser for each one is only a few lines, and the entire difference between them comes down to one stream decision function and a single boolean. But that tiny gap is exactly where the bugs hide. For instance, Emby will report a stream as a DirectStream, as if it is remuxing the file, when it is really just a plain direct play. Jellyfin does not do that. So if you trust the label you end up calling a direct play a light transcode, and I had to special case it in a few different spots to get it right. On top of that, every server has to produce the exact same canonical session shape, whether it came from Plex, Jellyfin, or Emby, and that shape is opinionated enough that these little parity bugs surface fast.
Following up on that, how do you handle differences in the APIs and data models of those media servers? I assume Plex’s API might be more mature than say Jellyfin’s.
Conceptually they are pretty similar, which is to be expected since there are only so many ways you can model media libraries and their metadata. Jellyfin being a fork of the public Emby release does lend them a fair share of similarities, but also a shared immaturity compared to Plex.
One of the best things about Plex is its authentication and user management. From a server hosting perspective it is great because Plex can be the authoritative body, so you just sign in and pair your Plex server to Tracearr, or anything else for that matter. Jellyfin and Emby follow the traditional self-hosted path, which requires access to the host, or an address that resolves to it, plus an API key. There are slight differences in the expected headers between the two, but otherwise pairing them is pretty much identical.
Where things really start to deviate is in retrieving live events and data, and this is where Plex’s maturity really stands out. Plex, Emby, and Jellyfin all let you poll for that data, so fetching active sessions, knowing when they end, and everything in between. The downside of polling is that you do not know what changed until you ask, so you only get things as fast as you can poll. It is worth mentioning that both Jellyfin and Emby have webhook plugins, but in my opinion the reliability and performance are not worth the extra setup. Plex is way ahead here because they support server side events natively. That lets an application like Tracearr subscribe to events like play, pause, skip, and so on, and Plex tells Tracearr the instant those things happen. That is what makes it feel instant, and makes the Tracearr dashboard a real live view of what is happening at any given moment.
Now I will follow that up by saying I have already built a separate project that adds API configurable server side events to both Jellyfin and Emby. It is a plugin under the Tracearr org called Media-Server-SSE, and that side of it is done. What is left is the Tracearr side, wiring it in so the live, event-driven experience you get with Plex today comes to Jellyfin and Emby too. Stay tuned.
Everything else is standard enough to let us share a lot of utilities. There are some quirks in things like episode fetching, and even some cases where Jellyfin and Emby start to deviate from each other. But in 90% of cases the lift feels more like supporting two different services rather than three. Still, we keep finding skeletons as we work down the roadmap and add new data layers.
The account sharing detection engine has six distinct rule types, with “Impossible Travel” being the most clever. Walk me through how that works under the hood. What data points are you correlating and are you doing any sanity checks to avoid false positives?
So before I get technical, I think the why is equally as important as the how. This was one of the first solves for me that really started to bring Tracearr to life. Generally speaking, GeoIP is not a reliable technology. Between things like CGNAT, VPNs, and constantly rotating IPs, it is really hard to say “this IP is exactly here” with any real accuracy. So instead of polling ten different IP lookups and blasting my users’ IPs out to a bunch of services just to get back a pile of conflicting answers, I decided that relativity was going to be the most powerful tool. Boiled all the way down, it is detecting two streams whose GeoIP results are so far apart that it would be physically impossible for a person to travel that distance in the time between them. Like New York to London in three hours. We did quickly run into people falsely triggering it when they connected to a VPN, but honestly that just helped us harden the rule, by letting us exclude the cases where the device id does not change, since that is almost always a VPN switch on one device rather than a person actually moving.
Now for the nerdy bits. Haversine is the standard formula for the great-circle distance between two lat-lon points on a sphere, basically the straight line distance between two GPS coordinates once you account for the curve of the Earth. So when a new stream starts, I look up the IP against the MaxMind database that ships with Tracearr, which is a totally local lookup with no external call. Then I pull that user’s streams from the last 24 hours, throw out anything on the same device, and grab their most recent previous stream. I calculate the Haversine distance between where they were and where they are now, divide it by the time between the two streams, and that gives me an implied travel speed. If that speed comes out faster than the threshold, it fires a violation.
I set that threshold at 500 km/h, which is well past anything you could realistically do by car, train, or boat, so normal travel on the ground never trips it. It is also configurable, so a setup full of frequent flyers can loosen it and a stricter one can tighten it. The real safeguard against false positives though is that same-device exclusion. If you actually fly somewhere and keep watching on your own phone, it is the same device, so it gets ignored. The rule really only fires in a pretty specific situation. You have got the same account showing up in two places that are way too far apart, on different devices, and the time between them is way too short for anyone to have actually made that trip. And if the lookup cannot place an IP at all, like a private or local network address, there is just no location to compare, so it stays quiet instead of guessing.
When it does fire, the violation captures both locations, the distance between them, the actual time gap, the calculated speed, and the threshold it broke. So the detail page lays out exactly what happened and you get to make the call yourself. I really did not want it quietly banning people off a GeoIP guess that is wrong as often as not.
For a bit of context, impossible travel is one of six detection rules, alongside things like concurrent streams, simultaneous locations, device velocity, and account inactivity. Each of those original rules is its own dedicated check like this one, but there is also a newer condition based builder now that lets you assemble your own rules out of composable pieces. Under that system, impossible travel is really just a travel speed condition with the same-device exclusion switched on.
Given the shared TypeScript monorepo, how much code are you actually able to share between the web frontend and mobile, and what’s been the biggest surprise so far developing the mobile app?
More and more each release. Early on there was a ton of duplication, mostly because of my own early struggles with React Native, which honestly are still there, but I am making a lot of progress. Inside the repo we have a shared directory that holds types, schemas, constants, helpers, and more. That is how we make sure that anytime we are querying, pulling objects apart, converting units, or humanizing text, it is done identically on both platforms. It took a lot of design and iteration to get that shared package into a good place though, because it has zero React or React Native dependencies. Its only real dependency is Zod. That is the only way to do it without the two platforms’ libraries stomping on each other.
The other massive chunk of shared code I want to call out is translations. That one took a bit to get off the ground, but thanks to our community and the Crowdin platform, it feels amazing to say that Tracearr on web and mobile is available in over 30 languages at over 70% completeness. That has been a huge milestone in making the platform more accessible.
The biggest surprise developing the mobile app, without a doubt, was the platform experience on both Google and Apple. Google basically makes you doxx yourself if you do not have an LLC, which is absolutely wild. And I am clearly not the only one bothered by this kind of thing, the whole Android developer verification push turned into a real fight in 2026, big enough that Google ended up walking parts of it back. You also have to own an Android device to get verified, so you can then go doxx yourself online. Shoutout to IamSpartacus for sending me that S8 to get me going. After that it was fine, the app was approved pretty quickly once I got through the 15 day beta jail, and it was uneventful. Apple does not make you doxx yourself, but the submission process was unbearable. Long review times followed by pretty sub-par feedback, and it took a couple of rounds of just resubmitting the same thing to get it through, since everything they wanted clarified was already in there to begin with. I will end this one by saying that other than Google still making me doxx myself, both platforms have been quick and reliable for every release since.
Tracearr has 19 contributors already, and it’s only a few months old. Managing open source contributions is a whole skill set beyond just writing code. Any particular strategies you’ve found effective for keeping the project organized, reviewing PRs, and maintaining a welcoming community? Any fun or interesting contributions that have come in that you didn’t expect?
This is a loaded question, and I will be honest, I do not think I am managing it perfectly right now, but I am actively trying to find better ways to support the people who contribute. Tracearr launched publicly in December of 2025, and in that first month I was so eager to build a supportive community that I was probably too lenient. I sacrificed some quality just to bring more people in, and I kind of backed myself into a corner doing it, because then I had to turn around and be the bad guy when people were racing to slam in any feature request that came through.
It was actually a good wake up call. I have always wanted Tracearr to be flexible, but what I care about more is that it stays intuitive and fast and does not break. That creates real friction sometimes. Somebody will ask for something like rules based on what state a user is in, which sounds reasonable on the surface, but GeoIP is far too unreliable for that, so it might only work well for ten percent of the user base, and shipping it would just make the product worse for everyone else. So I got stricter and standardized the repo with a real contributing guide, and a big part of that guide is propose the feature first. Talk to me before you write the code.
The other big driver behind that rule, and maybe I am a little grumpy about this one, is vibe coding. More and more PRs come in that just lack any real understanding of the codebase. It is some “I prompt therefore I am” stuff, where someone shoehorns a feature in and the only reasoning behind it is “because I wanted it.” I actually went and checked whether I was just being a grump, and honestly, no. This has become one of the biggest open source maintainer complaints out there right now. curl killed their entire bug bounty program over AI slop, and there is real research showing these AI PRs pull something like four times the review comments and sit open five times as long. Generating the code dropped to basically free, but reviewing it and actually owning it got more expensive, and all of that cost lands on me. So proposing first is partly just me protecting the quality bar and my own sanity.
I do not want to make it all sound negative though, because the other side of this has been genuinely awesome. The contributions I did not see coming are the ones that made Tracearr what it is. The biggest by a mile is a contributor who goes by durzo. He has basically acted as an infrastructure and operations co-maintainer, quietly building out the whole self-hosting reliability layer I had not gotten to yet. Docker hardening, a maintenance mode and health check system, backup and restore, Tailscale support, network monitoring, DNS caching. A huge part of why Tracearr actually runs well in someone’s homelab is his work.
And he is not the only one who has made this better. A lot of the contributions I did not see coming have been exactly that kind of homelab tier work I could never get to myself, Docker and Postgres edge cases that only surface on real self hosted setups, tooling fixes that unblocked a whole group of Windows based contributors, richer network and ISP data attached to every IP address, and extra notification integrations for the homelab crowd. The pattern I keep noticing is that the best contributions come from people scratching their own itch in the exact spots I cannot get to. My job is mostly just keeping the door open and the test suite honest, and getting people real feedback fast.
What are your future plans for Tracearr? Any major features or integrations on the roadmap that you’re excited about?
Yeah, there are a couple of things on the roadmap I am genuinely excited about, and they both come back to users.
The big one is true multi-user support. Right now Tracearr is really built around the person running the servers, the admin who sees everything. What I want is for the actual members of your server to be able to log in and see their own stats, their own watch history, their own activity, all of it. So it stops being a thing only the owner really logs into and actually becomes useful to everyone on the server.
The other one I am excited about is merging user data across platforms. A lot of people who share libraries run more than one media server, so the same person might have access to both your Plex and your Jellyfin. Today those show up as two completely separate users. I want to be able to link them, so if one person is on Plex and Jellyfin, all of their activity lands in one place and you get a single, unified picture of what they are actually watching, no matter which server they used it on.
If you could go back to day one of the project with what you know now, what would you architect differently?
Honestly the thing I would change is the rules engine. The first version I built was pretty rigid. Every rule was its own hardcoded type with its own dedicated evaluator and its own little schema. Impossible travel had its own evaluator, concurrent streams had a totally separate one, and every rule was like that. It worked, and it got the core sharing detection out the door fast, but it did not scale well. Every time I wanted a new kind of detection, I was basically writing a whole new evaluator from scratch.
What I eventually moved to, and what runs today, is a condition-based engine. Instead of hardcoding each rule, rules are built out of composable pieces, a field, an operator, and a value, that you can mix and match. That is dramatically more flexible. It lets people assemble their own rules instead of waiting for me to code up a new type, and it covers way more ground with way less code.
If I could go back to day one, I would have just started there. The problem is I built the rigid version first, so I had to build the flexible system alongside it, write a migration to move everything over, and I still have some dead code from that first version sitting around. It is not the end of the world, but it is exactly the kind of thing where, if I had seen where it was heading, I would have skipped a whole generation of the design and saved myself the rework.
Lastly, for anyone interested in building their own open source project, what advice would you give based on your experience developing Tracearr so far?
Honestly, my biggest piece of advice right now is shaped by the fact that the open source space is absolutely flooded with AI junk. So the first thing I would say is build something real. Something you actually use and understand, not just whatever you can get an AI to spit out.
And then, do not be in a rush to share it with the world, especially if it is vibe coded. This is the part that genuinely worries me. The track record on vibe coded apps getting pushed out publicly is rough. There are a stack of documented cases just in the last year of these things leaking entire production databases, exposing hundreds of thousands of API keys, and shipping with the database security straight up turned off. One report found something like one in ten apps built on a popular AI builder were leaking user data through the exact same flaw. The reason is simple. AI is great at getting something to work. Getting it to work safely is a completely different thing, and it has no idea which one it just handed you. If you do not actually understand what you shipped, you are in no position to be sharing it, because you cannot stand behind it.
The other thing I would say is collaborate where you can. Part of what got me here was being willing to look hard at what already existed before reinventing it, and a huge amount of what makes Tracearr good came from other people jumping in on the exact spots I could not get to. You do not have to build every piece yourself, and you definitely do not have to build it alone. Find the people already working on the problem and work with them where it makes sense.
So yeah. Build something you actually understand. Do not shove it out the door just because the tools let you. And work with people where you can instead of just piling onto the noise.
Closing Thoughts
It was great chatting with Connor and getting a peek behind the curtains. Tracearr is clearly a passion project driven by the needs of the community. If you haven’t checked it out yet, head over to tracearr.com or join their Discord to follow the development.
Do you have any questions for the Tracearr team? Drop them in the comments below!

