Self-hosted service
Silo-Server/silo-server avatar
Silo-Server/silo-server

Silo: a pre-release self-hosted media server with a Jellyfin compatible API

Self-hosted media streaming server with a Go backend, React web UI, Docker deployment, transcoding, and Jellyfin-compatible APIs.

707 stars90 forksGoAGPL-3.0

At a glance

What is it?
Silo is a Go and React media server for films, series, audiobooks, ebooks, podcasts and manga, deployed with Docker Compose alongside PostgreSQL with pgvector and Redis, and it speaks the Jellyfin and Emby compatible API so existing clients can talk to it. The README is blunt about two things: the project is pre-release with changing APIs and migrations, and the 1.0 support promise covers only movies and series.
Who is it for?
Adopt Silo if you want to self-host your media library, you are willing to run a Postgres and Redis alongside it, and you want your existing Jellyfin or Emby clients to keep working against a server you control.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Pre-release, and a 1.0 scope narrower than the feature list

The README opens with a warning that sets the expectations for everything else. Silo is pre-release, APIs, configuration and database migrations may change before the first stable release, and you should back up your deployment before updating.

Then there is the scope statement, which is the most useful paragraph in the document. The 1.0 milestone covers movies and series. Audiobooks, ebooks and Audiobookshelf compatibility keep working as they do today but are labelled beta, they are outside the 1.0 support promise, and they will be replaced by a consolidated Books effort with no assigned release date. The broader capabilities listed in the README include those beta implementations, and the text says so explicitly rather than implying everything is supported in 1.0. Existing library data is preserved when upgrading.

That combination, a clear version ceiling and a clear subset, is unusual and welcome. A media server is infrastructure you run for years, and the failure mode of these projects is that the whole catalogue quietly rots when a rewrite changes the database layout. Silo at least tells you which parts are frozen and which are moving, and tells you that your existing library records survive the move.

The media types themselves are broad: a self-hosted media server for films, series, audiobooks, ebooks, podcasts and manga, all in one catalogue.

Quick start: a clone, an env file and compose up

The quick start requires Docker Compose 2.24 or newer, and the default stack runs Silo, PostgreSQL with pgvector and Redis. The sequence is:

sh
git clone https://github.com/Silo-Server/silo-server.git
cd silo-server
cp .env.example .env
chmod 600 .env
printf '\nPOSTGRES_PASSWORD=%s\nSECRET_KEY=%s\n' \
  "$(openssl rand -hex 24)" "$(openssl rand -base64 48)" >> .env

The chmod 600 on the env file and the generated secrets are the part to notice. The database password and the server secret are generated with openssl and appended to the file, and the user manual explicitly says to back up SECRET_KEY, storage, GPU acceleration, Meilisearch, external PostgreSQL and Redis, transcode nodes, PostgreSQL tuning, backups and updates.

After setting MEDIA_ROOT to the absolute path of your media, the stack comes up with:

sh
docker compose up -d

and you open http://localhost:8090 and complete onboarding.

The env file shipped in the repository is the configuration reference, and it is heavily commented. The values that matter on a first run are MEDIA_ROOT, the host path to your media library, and MEDIA_CONTAINER_ROOT, the container path where Silo sees that same library, with the comment noting that Continuum migrations should set the second one to the old in-container library path if existing library records use that path. That is a migration trap worth reading before you touch an existing install.

The rest of the file shows how the stack is meant to grow. COMPOSE_FILE layers a base compose file with docker-compose.vaapi.yml for Intel VA-API or Quick Sync, or docker-compose.nvidia.yml for NVENC, and the comment notes that leaving it unset gives the CPU only defaults.

The compose file shows what the stack really needs

The docker-compose.yml in the repository is more informative than the feature list, because it shows what each service is for and how carefully it is bound.

PostgreSQL runs on the pgvector/pgvector:pg18 image, so the vector extension is in the database rather than bolted on, and it is given a shared memory size of 8gb by default. Its port is published to 127.0.0.1 rather than to every interface, which means the database is reachable from the host but not from the network, and it has a healthcheck built on pg_isready.

Redis runs the alpine image, again bound to 127.0.0.1, with a redis-cli ping healthcheck. It is the piece that makes the scaling story work later, because it is the shared state that a separate transcode node can talk to.

Meilisearch is where the care shows. The image is pinned to v1.54.0 with the comment that Meilisearch will not open data written by another version, it sits behind a search profile so it only starts when you ask for it, and its entrypoint refuses to start without MEILI_MASTER_KEY. The .env file then says to set MEILI_UPGRADE_DB for one start after changing the image, and to remove it afterwards, which is the sort of instruction that only exists because somebody broke their index and wrote it down.

The Silo service itself runs the published image ghcr.io/silo-server/silo-server:latest, and the compose file carries a comment about what happens on stop: Silo fails readiness but keeps serving for SILO_SHUTDOWN_DELAY, which is how a rolling update avoids dropping in-flight playback.

Jellyfin compatibility, and the clients that depend on it

The connect column of the README's feature table is where the compatibility story sits. You can use the included web app, or the Jellyfin and Emby compatible API with clients such as VidHub, Findroid and Infuse, and the text adds that client coverage varies.

That is the pragmatic core of the project. A media server is only as good as the clients that can talk to it, and the existing Android, iOS and third party apps already speak the Jellyfin protocol. Supporting that API means Silo can replace a Jellyfin server without forcing anyone to change the app on their phone or television.

The repository also contains the web frontend and a contracts directory, and the Dockerfile shows how the two are built. The frontend stage uses node:22-slim with pnpm 10.32.1 and a frozen lockfile, and the runtime Jellyfin Web installer uses node:24-slim, with the comment explaining that Jellyfin Web 12.x requires Node.js 24 or newer independently of the Silo frontend toolchain, and that the installer runs the npm release each Jellyfin Web version declares in its engines.npm. There is also a frontend_dist stage built from scratch so that CI can inject prebuilt assets.

The contract fixtures matter more than they look. The Dockerfile copies contracts/api/v2/fixtures into the build and the comment explains that the v2 contract fixtures are imported by the web tests, which type-check them as part of the build, and that they live outside web so they are copied explicitly. This is an API contract that the web client is tested against, not just a document.

The native clients are separate repositories, silo-apple and silo-android, and the README asks that client-visible API, authentication, playback or metadata changes consider both.

Scaling out means shared Postgres, Redis and separate transcode nodes

The scale column of the feature table describes the intended growth path: start with one integrated server, and split proxy and transcode roles across shared PostgreSQL and Redis when you need to.

That is a coherent design for a media server, because the two halves have different resource profiles. A proxy node needs to be reachable and cheap, since it just routes requests, while a transcode node needs the GPU. Splitting them means the transcoders can sit on machines with the hardware, and the proxy can sit where the clients are.

The Go dependency list backs the description. The server uses the chi router, pgx for Postgres, the Redis client, gorilla/websocket and a socket.io v2 package for realtime, Prometheus client_golang for metrics and OpenTelemetry for tracing, the AWS SDK v2 with the S3 manager for object storage, and golang-jwt for tokens. There is also huma, an OpenAPI framework, with kin-openapi, oasdiff for spec diffing and santhosh-tekuri/jsonschema for validation, plus open-policy-agent for policy evaluation. There is wazero, a WebAssembly runtime, and webpush-go for push notifications, and goose for database migrations, which is the piece that connects directly to the warning about migrations changing.

Two other dependencies are worth flagging because they are not typical for a media server. go-sqlite3 appears in the requirements, so there is a single node or development mode that does not need Postgres, and DataDog's go-sqllexer is there, which is a query parsing library usually used for log or query analysis.

The module targets Go 1.26.4, and the build container uses the floating golang:1.26 tag with the comment that this is deliberate so images pick up Go patch releases without a manual bump.

Build numbers, not versions, and an issue-first contribution model

The release vocabulary is unusual and worth understanding before you deploy. Until the first release, default branch images carry an ordered build-N tag and a short commit SHA alongside latest. Build numbers order published images, and they are explicitly not release versions. The release versioning document defines each tag and the SemVer contract.

So today, the image you pull by latest is a moving target built from the default branch, and the only way to pin is to pin a build number or a commit SHA. That is a direct consequence of the pre-release warning, and it is a real operational cost: your deployment is not reproducible unless you choose a tag and keep it.

The contribution model is also stated unusually plainly. Right now, the most helpful way to contribute is a clear, accurate issue, because changes are implemented and reviewed through the maintainers' own workflow and pull request volume has outgrown what they can review carefully. Pull requests are still welcome from contributors who have had one merged in a Silo repository, or when a maintainer asks for one, and other pull requests are closed without review. The issue forms ask for reproduction steps and raw logs.

That is a maintainer protecting a small review capacity, and you should read it as a signal about the pace of the project rather than as a brush-off. Questions and discussion go to Discord, and bugs, install problems and performance issues go to the GitHub issue forms.

The licence is AGPL-3.0-or-later, per the badge, with a LICENSE file, a THIRD_PARTY_NOTICES.md and a TRADEMARK.md at the root. For a self-hosted service that you run yourself, AGPL is workable, but if you intend to modify it and offer the result to others over a network, the obligations are the thing your legal team needs to see before you start, not after.

Editorial conclusion

Adopt Silo if you want to self-host your media library, you are willing to run a Postgres and Redis alongside it, and you want your existing Jellyfin or Emby clients to keep working against a server you control. Do not adopt it if you need a stable release, since the README warns that APIs, configuration and database migrations may change before the first stable release, and do not count on the audiobook, ebook or Audiobookshelf support being maintained, because those are labelled beta and outside the 1.0 promise. Verify first that the media root paths in .env line up between MEDIA_ROOT and MEDIA_CONTAINER_ROOT, that you can back up the SECRET_KEY, since losing it breaks session and token handling, and whether the pre-release build-N image tags are acceptable for a deployment you care about.

Frequently asked questions

Is Silo stable enough to run?

The README warns that Silo is pre-release, that APIs, configuration and database migrations may change before the first stable release, and that you should back up your deployment before updating. Existing library data is preserved when upgrading, and the 1.0 milestone covers movies and series only.

How do I install Silo?

You need Docker Compose 2.24 or newer. Clone the repository, copy .env.example to .env, restrict its permissions and generate a POSTGRES_PASSWORD and SECRET_KEY into it, set MEDIA_ROOT to your media path, then run docker compose up -d and open http://localhost:8090 to complete onboarding.

Which clients work with Silo?

The included web app, and the Jellyfin and Emby compatible API with clients such as VidHub, Findroid and Infuse, though the README notes that client coverage varies. Native clients are developed separately in the silo-apple and silo-android repositories.

What does Silo need besides the application container?

The default stack runs Silo with PostgreSQL with pgvector and Redis, and Meilisearch is available behind a search profile with the image pinned to v1.54.0 because Meilisearch cannot open data written by another version. The compose file publishes database and Redis ports to 127.0.0.1 only.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. README
  4. Silo-Server/silo-server on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/silo-server-silo-server.svg)](https://hysenlabs.com/projects/silo-server-silo-server)

Was this useful?