12 min read

On The Topic of Selfhosted

A walkthrough of Kallax features, how the project is setup and future goals.

I recently received some unexpected interest in the hobby board game collection and event organizer, Kallax, from the community over at /r/selfhosted. It sparked a thoughtful discussion about the future of the project.

The post received 397 upvotes and 21 downvotes, with 48 people explicitly stating they would run a self-hosted version of Kallax if it was available. That is a large enough group for us to take the request seriously, so I have been discussing the possibilities with my co-developer.

After careful consideration, we've concluded that we won’t be able to deliver at this time without compromising the services provided by the hosted version.

The Why Behind
What Keeps a Hobby Project Going for Years?

I wrote a post (linked above) explaining my personal motivation behind Kallax. The project was never built with self-hosting in mind, so I will start by walking through some of the features we would have to modify or disable to support it.

This post will also provide some insight into how we are setup for the technically interested. I’ve kept things concise and simplified, some parts might be hard to follow.

Basic Setup
Kallax is a Blazor Hybrid project and can be divided into four main components:

  • webserver - responsible for hosting the backend API, communicating with the database and serving an initial pre-render of the page. Running .NET 8
  • client - your browser downloads a WebAssembly build from the webserver and runs directly in the browser. This is responsible for rendering the site and interactive functionality. It relies on the webserver for reading and modifying data, but caches aggressively for the session.
  • service worker - we run a background service on the server that runs various tasks in bulk. It modifies things directly in the database and never communicates with the webserver or clients directly. Running .NET 9
  • database - Postgresql 17.4 with hourly backups to nearline storage and daily to offsite data storage (which is self-hosted).

Notifications
The notifications (top-left corner on the site) comes from two different sources. The client will generate notifications itself, for example if you have pending friend invites. If you see the notification it means that the invite is already downloaded and cached, so we are ready to serve it under /friends.

The other source is this website (my blog). It is powered by Ghost which is designed as a headless CMS but can also host primitive blogs. If I tag this post with #klx-notify then it will be visible for the Kallax client and it will show a little notification.

Kallax embeds the blog posts directly under /house-rules so you don't have to leave the site and I often hide them on this site with the tag #hidden to prevent spamming my blog with development updates.

A selfhosted version would likely just disable notifications for dev logs.

Image Optimization
We aggressively optimize images. Kallax only serves images in .webp format which is widely supported by browsers now. For image optimization, we rely on a third-party provider. Selfhosted solutions would have to provide their own API key, replace this with a selfhosted alternative or store in original format.

The size of these three images, from left to right, is 50.5KB, 67.9KB and 66KB.

The quality might not meet an artist’s standards, but if you squint a little and use your imagination, it’s good enough to recognize which game it is.

Transactional Email
Email is optional at account creation and we only use it for account recovery. We recently changed this to SMTP so it's easy to plug in your own credentials.

Database
Not much to say, we use Postgresql 17.4. You would have to run one yourself, but it's part of our docker-compose so that's not really an issue.

BoardGameGeek Verification
We recently introduced the ability to verify your BoardGameGeek username. Verifying gives you a little indicator on your profile, allows others to find you by BGG username and functions as an alternative to email for account recovery.

This setup relies on a trusted bot and an email inbox with read-access. We have permission to run this. It is fairly easy to configure, but might need to be disabled outright for selfhosted solutions depending on what BGG mods say.

Data
We aggregate data from several sources. A major one is BoardGameGeek (surprise) and while their API is open for now it seems those days are coming to an end as they will be requiring registration soon.

Kallax also incorporate data from TableTopia, Board Game Arena and Board Game Atlas (which is offline now). We have an agreement with Board Game Arena.

On top of this we enrich the entries with custom data. A large part of this is automated and handled by our service worker. An example of automated enrichment is similar games and popularity score.

A self-hosted aggregator would likely have to be a watered down version, but relying solely on BGG would probably be good enough. Alternatively, we could be used as the data source. We would have to confirm that we are allowed to do that and it somewhat defeats the goal of not relying on our service.

Log Management
We use a third-party provider for log management. The project uses Serilog, so selfhosted solutions could change the sink to whatever they please.

In the interest of transparency, this is what a logged message looks like.

{
  "dt": "2025-04-25T11:51:11.7007753Z",
  "level": "INFO",
  "message": "\"/kallax.GameInstanceAccessor/Get\" took 3 ms",
  "messageTemplate": "{method,-50} took {execution,-4}ms",
  "properties": {
    "ConnectionId": "0HNC2GQ25UHGB",
    "RequestId": "0HNC2GQ25UHGB:00000001",
    "RequestPath": "/kallax.GameInstanceAccessor/Get",
    "SourceContext": "KLXApp.gRPC.PerfLogger",
    "execution": 3,
    "ip": "REDACTED",
    "method": "/kallax.GameInstanceAccessor/Get",
    "user": "1"
  }
}

We run some basic analytics mainly aimed at checking that the server is doing okay (reasonable response times) and seeing if people are signing up.

As you can see, we do use IP addresses. They used for the graphs above and to detect suspicious behavior. More importantly, it's required for our firewall and rate-limiting to work proper on the server.

We don't have any traditional tracking (Google Analytics) and the client only contacts the server for data read/write, so we don't really have any insight into where users come from or what they are doing on the site.

Your IP address is not stored in the database and only exist in logs. Our log retention is 3 days, after that period we have no idea who you are (unless we have your email or BGG username).

Legacy
The server is hosting another API as well that was used by our legacy version of Kallax. The legacy version ran .NET 6 BlazorWasm and was served statically (no pre-rendering) from a S3 bucket and later directly from our CDN provider.

The two solutions have been running in parallel, both hosted by the webserver, since August 2024. We discontinued the legacy client 3 days ago and are cleaning up the server code to disable the endpoints.

Features that benefits from a central hub
The friend feature naturally benefits from being centrally hosted, but you could simply invite your friends to join your local instance instead.

More importantly, we’re currently testing Kallax with a few board game cafés and plan to introduce support for places soon. The idea is that both public spaces (like cafés or libraries) and private ones (such as workplaces or private clubs) could share their collections on the platform. No need to buy a game if you can just drop by your local café.

Places have opening hours, and some may have entry requirements like memberships, paid entry, or needing to buy a drink. We want to support these scenarios well before a broader rollout, which is why we’re working with a few cafés to make sure it fits their needs.

We're also developing support for public events. The main hurdles right now are address geocoding and getting more users to actively use the event feature. The goal is allow you to discover board game events happening nearby.

Most of these social features would either be disabled in self-hosted setups or would need a way to sync data from our servers.

Feature Summary
There is quite a lot of features that would need to be modified to some extend in order to support a selfhosted version. None of them impossible obstacles to overcome, but it is a bit of a death by a thousand cuts situation.

We are more cautious of this now and have started to rewrite some of these features to be more provider agnostic. The path here is longer than we expected and we are currently developing features benefiting from a centralized solution.

Open Source

The project is currently "proprietary" in the sense that only a small circle of developers have access. We have also received some requests to go open source, so I want to address that topic here. I personally see this interest as falling in three different categories. I encourage all of them.

Spectators
Looks at the code out of curiosity and/or because they want to know what they are running on their machine. A sensible policy really, especially if you compile the code yourself too.

Tinkerers
Forks the code and modifies a bit to suit their own needs. Some comments on the post fell in this category, people who wanting to host the project but for (...).

Contributors
Opens GitHub issues, implements features, makes pull-request and take part in shaping the project - or share their bug fixes with the community.

Personally, I'm more of a tinkerer than a contributor.

Spectators and Tinkerers don't necessarily need true open source projects, they need the source code to be available (non-collaborative open source).

Contributors are awesome and can super charge a project, but they are rare and managing an open source project is a whole different ball game than sharing a project with friends. You need proper guides on how to get started, information on contributing and someone to enforce the project standards.

We’ve previously experimented with opening up contributions on an invite-only basis, and it was not for me. What was used to be a free space for coding became more about onboarding, code reviews, and delegation. I do enough of that at work.

I’m a Lead Software Engineer, and my trusted co-developer and friend is Team Lead. Don’t ask me what the difference is, I am not entirely sure myself.

The project is currently too much of a technical playground for the team, making the experience less enjoyable for outside contributors. This is based on an actual (translated) conversation we’ve had.

Can I refactor the project structure to be feature-based instead of layered? We are discussing it at work and I want to try it out.
Yeah sure, just let me merge my branch first.

Now, we could mature the project, stick to core principles, write onboarding documents, implement more fail-safes, and accept that major technical decisions can't be made overnight anymore but requires community involvement.

It would mean sacrificing our digital sanctuary with no guarantee that significant contributions would actually be made as a result of going open source.

Anyway, this is not a definitive no. It is more of a stream of concision.

Source Code Access
The light version of open source is saying here is the code, enjoy. I have a framework for StarCraft II bots in this state. Please take it, run with it, share it as your own.

One option would be to put Kallax in this category. It lets people poke around, run their own forks and offers full transparency. From a somewhat selfish project perspective, this doesn't really bring much value back.

There’s also the risk that someone could copy the project and launch a competing site or commercial version. However, I believe this fear is often overstated. Don’t get me wrong, I’d hate to lose to myself, but if someone can outdo us at hosting our own service that we offer for free, then so be it.

We would need to adjust our practices a bit. One of the things we do is soft launch features by releasing them but not sharing the link. It allows us to ask select users for feedback before releasing it to everyone, but that does not really work if your source code it public. The solution here is just adding feature flags.

Many of our identifiers are also sequential and deterministic. It is not really a secret, I have a blog post about it here - the slugs are used for public resources so sequence quessing is not a major concern. It's one of the things I would change before releasing the source code though.

I would probably also give the code a pass and remove to-dos for my own vanity.

When it comes to releasing the source code, it’s a matter of weighing the required effort against the potential outcome. If it helps or inspires other projects, that would be great. But if it only makes me feel self-conscious without any real benefit, I’d prefer to stick with being transparent here on my blog.

However, one concern that I think is very fair is what if you abandon the project? I have covered in another post why I don't see myself abandoning it anytime soon, but the only thing you can guarantee in life is that nothing is guaranteed.

So, I pledge to release the source code in full if the project is discontinued. I have been trying to find online pledges for this concept and potentially automated tools to fulfill this pledge (in the case of death?). Message me if you know of any.

Possible Solutions

Now that you have an overview of the features we can start looking into the different options for making this work on a selfhosted setup.

Complete Standalone
With some effort, we could offer a self-hostable version of the current production build as-is. However, I doubt it would be very useful nor appreciated, since the setup has quite a few dependencies. These self-hosted instances would also include increasingly amounts of dead features as we add social features.

This approach might make more sense if Kallax were a finished product, but it's still in very active development. We've had three releases this week, and each one actually includes two or three smaller updates that I bundle together in the release notifications after the fact but release immediately.

Light Version
An alternative approach is splitting private and social features. Effectively creating a light-version (Eket?) that can handle collections, search and events but either relies on the main server for the social features or disables them.

This provide data ownership and provides partial protection against service disruptions. However, it does require a substantial amount of work upfront to split and, more importantly, adds a constant development overhead as our main service needs to support backwards compatibility indefinitely.

Another hurdle is that our internal ids are integers, so merging different environments is not trivial. The best solution here would likely be to opt for GUIDs instead or split the data on a table level between shared and local.

API and a new Open Source project?
A third option, and one that might be more achievable, is for Kallax to provide a public API and then help launch an open source initiative.

Two years ago alexalexalex09 created a Chrome extension (discontinued) that used our site as a backend and u/MetalMeeple on Reddit has read-access to board game data. We could expand on that to become an open API if there was an interest for it.

As I have stated in the open-source section, I am not the right person to run and maintain the open source initiative, but I will gladly aid in getting it started.

Conclusion

I honestly didn’t expect the level of interest the project would receive, and I severely underestimated the effort required to make Kallax self-hostable.

With just two developers, trying to support a self-hostable version would stretch us too thin and ultimately benefit no one.

If someone is willing to lead an open-source and selfhosted initiative then get started, you have proof of concept, an initial userbase and the support of two developers who have already built the product you are trying to make.

So, is that it? A 2881 word no?
I guess, at least for now. I wanted to give a proper answer as I feel like people invested enough time into that discussion to deserve it. Hopefully you found this article at least a tiny bit interesting too.

I have implemented a simple way to export your collection as a small token of goodwill. It's an early prototype for now and it needs to support more fields etc.

The idea is to give you an excel-like overview of your collection, with a handy CSV download so your can move your data to another platform or throw into a fully-fledged spreadsheet tool. Read the release note for it here.

The current version is read-only but the goal is to support in-line editing.

Beta version of advanced collection view and export