---
title: "Go dominates my homelab: a self-hosted app catalog"
description: "A catalog of the self-hosted apps in my homelab and why Go dominates the infrastructure layer: a single binary and small images."
date: 2026-10-03
locale: en
tags: [homelab, go]
summary: "A catalog with language, image size, and RAM at rest for the 24 apps in the homelab: 13 in Go, 11 of the 15 infrastructure ones. The self-contained binary and low memory footprint explain why Go dominates the layer you administer daily, while heavy media services (Jellyfin, Immich) stay in other languages."
url: https://escribano.dev/en/blog/020-homelab-go-stack-catalog/
---
The homelab has three machines. kvothe (Ubuntu, Docker) runs the media and the utilities; kilvin (Fedora, Podman and quadlets) carries the infrastructure and identity; elodin (QNAP) stores data and backups. Put the containers from the first two in a table and sort them by size, and a pattern shows up: the lightweight infrastructure services are written in Go, and the heavy media ones in other languages.

This post is that catalog: the language, image size and RAM of every piece running today. It is not exhaustive and does not try to be; those three columns show the pattern on their own. The [homelab series](/blog/006-homelab-vpn-access/) already covered access, DNS, [proxy](/blog/008-caddy-reverse-proxy-pihole/) and [identity](/blog/009-homelab-identity/); here I pull the whole stack into one picture once those layers are solved.

## Go in the infrastructure

Go has become one of the dominant languages in modern infrastructure tooling. Several of the projects that defined the last decade of operations are written in Go:

| Category | Projects |
|----------|----------|
| Containers and orchestration | Docker, Podman, containerd, Kubernetes, k3s, Helm, Kustomize |
| Networking and proxy | Caddy, Traefik, CoreDNS |
| Observability | Prometheus, Grafana, InfluxDB, Telegraf, Loki |
| Databases and storage | etcd, CockroachDB, SeaweedFS, rclone, restic |
| Infrastructure as code | Terraform, OpenTofu, Pulumi, Terragrunt, Packer |
| Developer tools | Hugo, Dagger, gh (GitHub CLI) |
| Self-hosted apps | Syncthing, Zitadel, Forgejo, Memos, FileBrowser, PhotoPrism, Vikunja, Navidrome |

One note on InfluxDB: version 3 is rewritten in Rust; 1.x and 2.x are still Go.

Containers, orchestration, observability and infrastructure-as-code share a profile: software that runs all the time, in many places, with little maintenance and a binary you deploy without thinking. That is where Go fits. The counterexample exists too: nginx, Redis and PostgreSQL are written in C and hold their ground with a maturity Go has not displaced. Go has won mostly in the operations layer; in classic databases and web servers, C still rules.

## The data: Go by majority and by layer

My homelab is a small version of that pattern. Of the twenty-four applications in the catalog, thirteen are written in Go: just over half. In the infrastructure layer (proxy, DNS, identity, git, metrics, dashboards) it is eleven of fifteen. In media and documents the pattern flips: of the nine applications in that group, only two (Memos and FileBrowser) are Go.

I count as an application every service with a purpose of its own; databases, development and CI containers, and work-in-progress projects stay out. That filter favors Go, and I will say it plainly: the three databases in the catalog (MariaDB, Firebird and Postgres) are C or C++, so excluding them raises the percentage. It is a method decision; counting them would make the split less favorable. The trend holds either way: the closer to daily maintenance, the more Go; the closer to serving gigabytes of photos and films, the less.

## The catalog

Images are what `docker images` and `podman images` report (layer size, one architecture); RAM is `docker stats` and `podman stats` at rest, rounded. A few services (Caddy, Memos, FileBrowser, Prometheus, Grafana, Vaultwarden and Whisper) run on both machines; for those I give the range. I use rclone on demand for backups; since it is not a resident service, it stays out of the catalog.

### Infrastructure and platform

| App | Language | Image | RAM | What for |
|-----|----------|-------|-----|----------|
| node-exporter | Go | 26 MB | 17 MiB | Host metrics |
| DuckDNS | shell | 35 MB | 5 MiB | Dynamic DNS |
| oauth2-proxy | Go | 36 MB | 5 MiB | Forward auth |
| Syncthing | Go | 43 MB | 26 MiB | File sync |
| Caddy | Go | 59–63 MB | 12–14 MiB | Reverse proxy and TLS |
| Authelia | Go | 78 MB | 17 MiB | Forward auth and 2FA |
| Tailscale | Go | 112 MB | 43 MiB | Mesh and access |
| Forgejo | Go | 178 MB | 203 MiB | Self-hosted git |
| Zitadel | Go | 179 MB | 81 MiB | Identity (OIDC IdP) |
| Portainer | Go | 184 MB | 19 MiB | Container management |
| Homepage | TypeScript | 266 MB | 115 MiB | Dashboard |
| Prometheus | Go | 296–423 MB | 90–93 MiB | Metrics |
| Glances | Python | 502 MB | 90 MiB | System monitor |
| wg-easy | Node | 535 MB | 18 MiB | WireGuard with a web panel |
| Grafana | Go | 555 MB–1.07 GB | 109–156 MiB | Dashboards |

### Applications, media and documents

| App | Language | Image | RAM | What for |
|-----|----------|-------|-----|----------|
| Memos | Go | 66–69 MB | 12 MiB | Quick notes |
| aMule | C++ | 126 MB | 129 MiB | eD2k/Kad P2P |
| FileBrowser | Go | 192–219 MB | 24–35 MiB | Files from the browser |
| Vaultwarden | Rust | 243–249 MB | 10–14 MiB | Password manager |
| BookStack | PHP | 401 MB | 12 MiB | Documentation and wiki |
| Whisper | Python | 606–614 MB | 13–315 MiB | Transcription (STT) |
| Piper | C++ | 877 MB | 650 MiB | Voice synthesis (TTS) |
| Jellyfin | C# | 1.56 GB | 1.0 GiB | Media server |
| Immich | TypeScript (+ Python) | 3.5 GB | 385 MiB | Photos |

The databases behind those applications (MariaDB, Firebird and Postgres) do not count as applications; they are supporting infrastructure, and none of them are Go.

## Why Go fits the homelab

The explanation starts with the binary. Go usually compiles to a self-contained binary: the Go runtime ships inside the executable, so there is no external runtime to install, unlike Python, Java or .NET. Without CGO you can produce static binaries, though not every Go binary is static or free of system dependencies. The model reduces the dependencies that can drift out of sync between the machine and the container. In a homelab that becomes images of tens of megabytes: Caddy 63 MB, node-exporter 26 MB, Syncthing 43 MB, Authelia 78 MB. The ones over a gigabyte in my catalog are all media (Jellyfin 1.56 GB, Immich 3.5 GB).

RAM at rest follows the same logic. Caddy settles at 12–14 MiB, Memos at 12 MiB, Authelia at 17 MiB and Vaultwarden (Rust) at 14 MiB. Whisper jumps from 13 MiB at rest to over 300 MiB while transcribing, Piper (TTS) sits at 650 MiB, the Immich server starts at 385 MiB and Jellyfin hovers around a gigabyte. On a low-power mini PC, that difference shows up in how many services fit without swapping and in the power bill. I have it down to the number: kvothe has 16 GB of RAM, and around 12 GB stay free in normal conditions, with Jellyfin and a large chunk of the catalog running. That is the headroom for adding more services without worrying about RAM, as long as I aim for lightweight ones whenever I can.

The third piece is maintenance. Fewer moving parts means fewer things that break when you update. Whether that turns into drama-free updates owes more to containers than to Go: isolating the service is what lets me update and roll back when needed. Still, when the container depends on a single binary (as happens with Go and Rust), the image is far smaller and updates run faster.

## Three caveats to the pattern

Go does not explain everything. There are three caveats worth keeping in mind.

First: Go and lightweight do not always go together. Grafana (Go) weighs more than half a gigabyte, Prometheus (Go) between 296 and 423 MB, and Forgejo (Go) settles at 203 MiB of RAM despite being a single binary. Observability tools bundle the frontend and the plugins, and a git forge carries far more than the runtime. Go shrinks the backend; whatever you bundle on top still weighs what it weighs.

Second: compactness is not exclusive to Go. Vaultwarden, written in Rust, produces a 243 MB image and starts in 14 MiB, in line with the Go services on the lightweight end. Rust shares the reason (self-contained binary, small images, and a sensible consumption profile in services like this one), so Go's place on this axis is not permanent.

Third: media. Jellyfin (C#) dominates the heavy end and has no Go equivalent as complete for video. With Immich (TypeScript and Python) the argument is weaker: PhotoPrism exists, also in Go, with an approach and a workflow that differ from what I am after. I picked Immich because it is multi-user, although in practice it is only me; somewhere down the list is trying PhotoPrism or even Ente (whose server is Go too), at some point. The underlying criterion is operational friction: when a piece demands more RAM and more attention than its alternative, it shows in the table; when it does not, it stays.

## Closing

In the [RPS framework](/blog/002-rps-principle/), Go takes the rock slot: one piece that works, with little to break. For personal infrastructure, where maintenance is what decides, rock wins.

The catalog repeats the pattern. Eleven of the fifteen infrastructure services are written in Go. The lightest ones (node-exporter, Caddy, Authelia, Syncthing, Tailscale) stay at tens of megabytes of image and a few tens of MiB of RAM at rest; others such as Forgejo, Zitadel, Prometheus or Grafana sit well above that. The gigabytes and the high RAM concentrate in media: Jellyfin (1.56 GB, 1.0 GiB) and Immich (3.5 GB, 385 MiB). Language is one of the few decisions you cannot change by putting a reverse proxy in front, so it is worth checking before you deploy. In the layer I administer by hand, Go wins by majority; in the media layer, it depends on what each ecosystem has available.

This catalog is also a snapshot of what [digital sovereignty](/blog/005-digital-sovereignty/) means in practice: fifteen services on my own hardware, each with its maintenance cost in plain sight and a clear exit if something stops fitting.
