Self-hosted service
Studio-Saelix/sencho avatar
Studio-Saelix/sencho

Compose files stay on the host, and the 1:1 mount path is the rule that bites

Self-hosted Docker Compose management platform. For single or multi-host compose-first workflow.

502 stars22 forksTypeScriptAGPL-3.0

At a glance

What is it?
A self-hosted Docker Compose control plane that runs as one container, treats your compose files on disk as the source of truth, and turns every node into the same autonomous peer. The constraints that will actually stop you are a mount path rule, a Node 26 floor, and an outbound call to the vendor's own site.
Who is it for?
Adopt Sencho if you run Compose on more than one machine and want a graphical surface without giving up the files on disk, because the source-of-truth claim is the whole argument for it and the peer-node design avoids exposing a remote Docker socket. Do not adopt it if your compose files do not live under one root you can bind-mount identically, since the 1:1 path rule is not negotiable and the failure mode is a validation error at deploy time.
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 TypeScript, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • Studio-Saelix/sencho502 ★ · +135
Star history
Repository2026-07-302026-11-14
Studio-Saelix/sencho367502

135 Stars in the last 107 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The host filesystem stays the source of truth, not the database

Sencho is described as a Docker Compose control plane for DevOps engineers, platform teams, system administrators and homelab users. It runs as a single container on your hardware and provides an interface for deploying, editing files, watching logs, restarting containers, browsing volumes and recovering from failures.

The design commitment is in the next sentence, and it is the one worth checking against your own habits. The compose files stay on the host filesystem and remain the source of truth.

That is a different architecture from tools that import your stacks into a database and then treat the imported copy as authoritative. It means the platform is a control surface over files you already keep in version control, and it means a file edited outside Sencho is picked up rather than overwritten. The drift detection feature exists to make that visible: it compares running containers against the effective Compose model and flags exactly what changed.

The cost is that the file layout has to be conventional enough for a tool to reason about. Which is what the mount rules in the shipped compose file are for.

The 1:1 path rule, and the one mount whose right side is fixed

The bundled `docker-compose.yml` is more instructive than the feature list, because its comments explain what will break.

Three mounts are required or recommended. The Docker socket is required for container orchestration, and the data directory carries Sencho's internal database for alerts and settings, with a comment saying you may change the host side anywhere on your server but must leave the container side as `/app/data`.

The third is the rule that catches people out. The comment is labelled as the 1:1 compose path rule: you mount the root folder where all your compose files live, and the host path on the left must exactly match the container path on the right. The example given is `/opt/docker:/opt/docker`.

There is a reason. Compose resolves relative paths inside a project against the project directory, so if the container sees the folder at a different path than the host does, the bind mounts inside your own compose files resolve somewhere else and the stack either fails validation or, worse, silently mounts the wrong host directory.

Two optional mounts follow: media drives, which the comment says are mounted so Compose inside Sencho can validate paths during deployment, and an OpenZFS ARC block discussed below.

Every node is the same autonomous instance, and the primary proxies for it

Multi-node is described as part of the architecture from the start rather than added later. Every Sencho instance is the same autonomous node, whether it runs alone or as one of many in a fleet.

Joining a machine means installing a second Sencho on it and connecting the two with a long-lived API token. The primary dashboard then acts as an authenticated HTTP and WebSocket proxy across the fleet.

Two security properties follow from that arrangement, and both are stated plainly. Each node uses its local Docker socket, Sencho does not require SSH, and it does not expose a remote Docker socket on the network. For an untrusted link the advice is to use TLS, a VPN or a private network.

Nodes behind NAT or strict firewalls get a separate answer. The Pilot Agent establishes a single outbound WebSocket tunnel to the primary, so the remote host opens no inbound port at all.

The practical upshot is that the thing you must not do is point a remote Sencho at a Docker socket over the network, and the project's architecture exists so that you never have to. Remote updates are described in the same terms: pull the latest image and recreate any node from the Fleet view, no SSH session required.

Node-to-node traffic bypasses the rate limit, and one endpoint points at the vendor

The example environment file is where the operational detail lives, and two of its entries deserve a careful read.

The first is the rate limiting. There is a global limit of 200 requests per minute per user session in production only, keyed by user ID for authenticated requests and by IP otherwise, plus a separate polling limit of 300 for dashboard and status endpoints exempt from the global one. The comment on the polling limit notes that it should be increased for environments with many concurrent browser sessions behind shared NAT.

Then there is the exemption, stated without hedging: internal node-to-node traffic, identified by `node_proxy` tokens, bypasses this limit entirely.

bash
TRIVY_BIN=
SENCHO_ASSURANCE_API=https://sencho.io
API_RATE_LIMIT=200
API_POLLING_RATE_LIMIT=300

The second entry is the base URL for what the file calls the Hardened Build assurance API, pointing at the project's own website. For a self-hosted AGPL tool that is worth knowing about before you deploy rather than after. Alongside it sits an entitlement stub for non-production use with the values 1, entitled, unauthorized or unavailable, which the comment says is ignored when `NODE_ENV=production`.

Drift is checked in four places, including against the project's own website

Drift detection appears more than once in the feature list, and the repetition is the interesting part.

There is stack drift, which compares running containers against the effective Compose model and flags exactly what changed. There is documentation drift, where a stack dossier diverging from the running stack raises a flag. There is Blueprint drift, on the declarative fleet templates. And there is a fourth one that is easy to miss.

The repository's own package scripts include `catalog:drift`, which runs a file called `check-website-drift.mjs`, alongside `catalog:validate` for a canonical validation and `catalog:sync` to synchronise a feature catalog. The scripts live under a website catalog directory.

So the project checks its marketing site against its code. That is a small thing and it is a good sign: a feature list that drifts from the shipped behaviour is the standard failure mode of a product with forty documented capabilities, and this one has a build step for it.

The `.env.example` also documents a public URL variable for the primary instance, described as being baked into the pilot enrollment YAML so that pilots on a different network can reach the public host.

Base images are pinned by digest and native modules cross-compile without QEMU

The Dockerfile is where the supply-chain posture is visible, and two comments do the work.

The cross-compilation helper stage is described as providing xx-clang and xx-apk, and its FROM line is pinned to a digest, with the comment saying the digest is pinned to prevent silent base-image changes between scan and publish. The frontend and backend builder stages are pinned the same way.

The third stage says it is cross-compiled with no QEMU execution. That matters more than it sounds. The build runs on one architecture and the image is built for another, and the alternative, running the foreign architecture under emulation, is slow enough that people disable it. The native modules being cross-compiled are named: bcrypt, better-sqlite3 and node-pty.

The backend stage installs python3, make and g++ with apk before running npm ci, which is what those three modules need to compile. The npm configuration is also tuned, with a fetch retry maxtimeout of 120000 and 5 retries, which reads as an accommodation for a flaky mirror rather than a correctness setting.

A `trivy.yaml` and a `signatures/` directory sit at the repository root alongside a `security/` directory, so the scanning the Dockerfile comments refer to has its configuration checked in.

The ZFS ARC note explains a false memory alert most dashboards would send

One block in the compose file is worth reading on its own, because it describes a bug class rather than a feature.

There is an optional mount for ZFS hosts only, for OpenZFS ARC stats, described as being for ZFS-aware host memory. The comment explains why: the ARC cache is reclaimable but the kernel reports it as used, which can trigger false host-memory alerts.

That is a specific and correct kind of operational detail. On a ZFS host, a container dashboard that sums host memory the naive way will eventually page you about memory exhaustion at the exact moment the system is healthy, because the ARC is memory the kernel is counting as used and the filesystem is counting as available. Uncommenting the mount lets Sencho treat ARC as available memory, and the comment notes it is usually already visible inside the container and is only needed in some cases.

The other configurable detail in the file sits in the ports comment. Sencho Mesh runs on an internal Docker network that Sencho creates and joins on boot, so cross-stack mesh routing needs no extra host ports or firewall rules, and the mesh subnet defaults to 172.30.0.0/24 and can be overridden with a `SENCHO_MESH_SUBNET` variable if it collides with an existing network.

Releases are automated, the commits are linted, and the version is still below one

The repository is set up for mechanical releases. A `.release-please-manifest.json` and a `release-please-config.json` sit at the root, which is release-please, and a `commitlint.config.js` with conventional commits is enforced through husky via a `.husky/` directory and a `prepare` script that installs it.

That machinery explains the version numbers. The recent tags are v0.96.0, v0.97.0 and v0.97.1, all in the summer of 2026, and `package.json` carries 0.97.1 with the licence declared as AGPL-3.0-only. A project below 1.0 with release automation is choosing to signal that the interface may still change.

Two more runtime facts are worth noting. The `engines` field requires Node 26.0.0 or newer, and the Dockerfile builds on a node:26-alpine base, so the build machine and the declared floor agree. The `type` field is `commonjs` while the project is written in TypeScript, which means the backend compiles to CommonJS rather than shipping ES modules.

Test tooling is split by layer: `npm test` delegates into the backend directory, while `test:e2e` runs Playwright against the `e2e/` suite, with a `playwright.config.ts` at the root and a `.node-version` file pinning the expected runtime.

Editorial conclusion

Adopt Sencho if you run Compose on more than one machine and want a graphical surface without giving up the files on disk, because the source-of-truth claim is the whole argument for it and the peer-node design avoids exposing a remote Docker socket. Do not adopt it if your compose files do not live under one root you can bind-mount identically, since the 1:1 path rule is not negotiable and the failure mode is a validation error at deploy time. Two things to settle before the first deploy: whether the assurance endpoint reaching out to sencho.io matters in your environment, and whether the Node 26 floor in the build is compatible with the machine that will build the image.

Frequently asked questions

What does Sencho actually manage?

It is a Docker Compose control plane that runs as a single container and provides an interface for deploying, editing files, watching logs, restarting containers, browsing volumes and recovering from failures. The compose files stay on the host filesystem and remain the source of truth.

How does Sencho connect to a second machine?

You install a second Sencho on it and connect them with a long-lived API token. The primary dashboard then acts as an authenticated HTTP and WebSocket proxy across the fleet. Nodes behind NAT or strict firewalls use the Pilot Agent, which opens a single outbound WebSocket tunnel so the remote host needs no inbound port.

Does Sencho require SSH or expose a remote Docker socket?

No. Each node uses its own local Docker socket, Sencho does not require SSH, and it does not expose a remote Docker socket on the network. The README advises TLS, a VPN or a private network for any untrusted link between nodes.

What is the 1:1 compose path rule in the Sencho compose file?

The host path on the left of the mount must exactly match the container path on the right for the root folder where your compose files live, for example /opt/docker:/opt/docker. The data directory is the exception, since its container side must stay /app/data while the host side can move.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
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/studio-saelix-sencho.svg)](https://hysenlabs.com/projects/studio-saelix-sencho)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/studio-saelix-sencho.svg)](https://hysenlabs.com/projects/studio-saelix-sencho)

Was this useful?