My thoughts and tips from my work life.

Search This Blog

© 2001-2026 Erik Pitti, All Rights Reserved. Powered by Blogger.

Sunday, April 19, 2026

Why I Built My Own Help Desk (And Learned Go Along the Way)


At some point, every small team hits the same wall: someone asks "where do I send support requests?" and the answer is either a shared inbox that everyone ignores or a $200/month SaaS that's overkill for what you actually need.
I hit that wall, and instead of picking the least-bad option, I did what developers do: I built something. And because I wanted to learn Go, I built it in a language I'd never shipped production code in before.

The problem with existing options

The self-hosted help desk space hasn't changed much in a decade. osTicket is functional but feels like it was designed for a different era of the web. Zammad is more modern but heavy — it wants a dedicated server, a full Elasticsearch instance, and an afternoon of your life just to get it running. FreeScout is closer to what I wanted, but it's PHP, and every time I touched the config I felt like I was doing archaeology.
The SaaS options are fine if you're okay with your support data living on someone else's infrastructure and paying per agent per month forever. I wasn't.
What I wanted was simple: a help desk that installs in one command, runs as a single binary, and doesn't make me think about it after the first deploy.

Standing on the shoulders of giants

I didn't start from a blank page. Years of using help desks — good and bad — gave me a pretty clear picture of what I wanted to steal and what I wanted to leave behind.
  • HESK got two things right that most tools overthink: the ticket flow is dead simple, and custom fields let you capture exactly what you need without forcing you into a rigid schema. I kept both.
  • Remedy is the opposite of simple — it's enterprise software in the fullest sense — but its CTI model (Category, Type, Item) is genuinely useful for classifying tickets in a way that makes reporting meaningful. Once you've worked with a well-configured CTI taxonomy, the flat category lists in lighter tools feel like a step backward.
  • Freshservice showed that a help desk doesn't have to look like it was built in 2009. A clean, modern interface isn't a luxury — it's what determines whether your team actually uses the tool.
The goal was to combine all three: HESK's simplicity, Remedy's classification model, and Freshservice's interface sensibility — in something you could self-host and own outright.

Why Go (and why I didn't know it yet)

I'd been wanting to learn Go for a while. It has a reputation for producing fast, reliable, boring software — and boring software is exactly what a help desk should be. The standard library handles HTTP, TLS, and templating without pulling in a dependency tree. The compiler catches an entire class of bugs before they ship. And a Go binary is a Go binary: no runtime, no interpreter, no "which version of Node do you have installed."
So I made a bet: learn Go by building something real. Not a tutorial project, not a toy. A production app I'd actually run.

What it took

Learning a language by building a non-trivial system is humbling. Go's approach to error handling, concurrency, and interfaces is different enough from other languages that I rewrote core pieces two or three times as my understanding improved. The discipline the language imposes — explicit errors, no generics magic, dependencies passed by hand — forced me to think carefully about data structures and ownership in ways I'd previously been able to gloss over.
The surface area of a help desk is also deceptively large. Tickets going in and agents responding sounds simple, but you also need authentication that's actually secure (TOTP MFA, SAML SSO for teams that want it), SLA tracking so nothing falls through the cracks, email notifications that don't end up in spam, and a permission model that doesn't collapse the moment you add a second role.

AI as a collaborator

The piece I'm most proud of is the MCP server layer. Full disclosure: Go Help Desk itself was built with the help of Claude — so it felt right to make Claude a first-class citizen in the finished product.
MCP (Model Context Protocol) is a standard way to expose your application's data and actions to AI tools like Claude. With Go Help Desk's MCP server, you can ask an AI assistant to pull up open tickets, summarize a thread, or update a ticket status — without building a custom integration. That felt like a meaningful step forward over what the existing tools offer, and it's not something I would have thought to build if I hadn't been working alongside an AI the whole time.
Building with AI assistance is still a pretty new experience for most developers. I found it genuinely changed how I worked — less time stuck on unfamiliar syntax, more time thinking about architecture and what I actually wanted to build. For a first Go project, that was a meaningful difference.

What I shipped

Go Help Desk is live. It's open source (AGPL-3.0), self-hosted, and runs as a single Docker container backed by Postgres. Features in v1:
  • Multi-user with roles (admin, agent, end user)
  • TOTP MFA + SAML SSO
  • SLA tracking and breach alerts
  • Full REST API
  • MCP server for AI tool integration
  • WASM plugin support for extensibility
Setup takes about five minutes. There's a /setup route on first run that walks you through creating the first admin account — no manual SQL, no config file archaeology.

Would I do it again?

Yes, and I'd recommend it as a way to learn any language: pick a real problem you actually want to solve, and build the thing. The pressure of shipping something useful keeps you honest. You can't paper over a bad abstraction when you're the one who has to live with it.

Go turned out to be a good choice. And having Claude along for the ride made learning it a lot less lonely.




0 comments: