---
title: Self-Hosting Best Practices
sidebarTitle: Self-hosting
description: Volume mounts, Docker socket security, networking, and resource recommendations for running Sencho in production.
---

## The 1:1 path rule

This is the most common setup mistake. When Sencho deploys a Docker Compose stack, Docker resolves bind-mount volume paths relative to the **host filesystem**, not the Sencho container. If your compose files reference relative paths like `./data:/app/data`, Docker looks for `./data` on the host, starting from the directory where the compose file lives on the host.

This means the path to your compose directory **must be identical** inside and outside the container:

```yaml
# Correct - same path on both sides
volumes:
  - /opt/compose:/opt/compose
environment:
  - COMPOSE_DIR=/opt/compose

# Wrong - different paths, relative volumes will break
volumes:
  - /opt/compose:/app/compose
```

See the [Configuration guide](/getting-started/configuration#compose-directory-the-11-path-rule) for a detailed explanation with examples.

---

## Volume mount checklist

Sencho requires three volume mounts to function correctly:

| Mount | Purpose | Required |
|-------|---------|----------|
| `/var/run/docker.sock:/var/run/docker.sock` | Docker Engine access for managing containers | Yes |
| `./sencho-data:/app/data` | Persistent storage for the database and encryption key | Yes |
| `/opt/compose:/opt/compose` | Your compose project files (must follow 1:1 path rule) | Yes |

<Warning>
  The data directory contains your Sencho database (`sencho.db`) and encryption key (`encryption.key`). Losing this directory means losing your Sencho configuration entirely. See the [Backup & Restore guide](/operations/backup) for backup procedures.
</Warning>

---

## Docker socket security

Mounting the Docker socket (`/var/run/docker.sock`) grants the container the ability to manage all containers, images, volumes, and networks on the host. This is equivalent to root access on the host machine, regardless of which user the Sencho process itself runs as.

Because the Docker socket is already the effective privilege boundary, Sencho runs as `root` inside the container by default. This matches Portainer, Dockge, Komodo, and Yacht, and ensures Sencho can always write to your compose folders, even when a stack container (for example anything from `linuxserver/*`) has chowned its own bind mount. See [Configuration: Container user](/getting-started/configuration#container-user) for details on the default and the `SENCHO_USER` opt-out.

If your environment requires stricter isolation, the Docker socket itself is the right place to focus:

- Running Sencho on a dedicated Docker host so a compromise cannot reach unrelated workloads
- Using Docker's `--userns-remap` for user namespace isolation at the daemon level
- Running Sencho behind a reverse proxy with authentication (see [Configuration](/getting-started/configuration#reverse-proxy-setup)) so the UI is not exposed to untrusted networks
- Setting `SENCHO_USER=sencho` to drop privileges inside the container as a defense-in-depth measure, accepting that filesystem writes to permission-restricted stack folders will fail in that mode

---

## Docker socket proxy

A Docker socket proxy sits between Sencho and the Docker Engine. Instead of mounting `/var/run/docker.sock` directly into the Sencho container, the deployment mounts a proxy socket in its place, or points Sencho at the proxy with `DOCKER_HOST`, and the proxy forwards only the Engine API groups its configuration allows. This reduces the blast radius of a compromised Sencho container: an attacker who gains control of Sencho can still call the Docker API, but only the groups the proxy permits.

A socket proxy is defense-in-depth, not a security boundary. Docker API access remains highly privileged even through a proxy, and the full profile below is still root-equivalent in practice. Use the profiles as a starting point for sizing a proxy, not as a guarantee that a restrictive proxy makes Sencho low-risk.

Sencho has no socket-proxy setting or UI. The proxy is configured entirely in the deployment compose file, in one of two ways:

- Mount the proxy socket at `/var/run/docker.sock` so Sencho and the `docker` CLI reach the proxy through the default Docker endpoint
- Set `DOCKER_HOST` to the proxy address (for example `tcp://proxy:2375`) on the Sencho container so the SDK and the Compose CLI reach the proxy through the same endpoint

The second option matters because Sencho talks to Docker through two paths, and both honor the same endpoint:

| Path | Mechanism | Proxy-aware? |
|------|-----------|--------------|
| Inventory, stats, events, lifecycle, prune, interactive shell, mesh network operations | Dockerode SDK over the default Docker endpoint | Yes |
| Stack deploy, update, down, pull, compose logs and config | `docker compose` CLI, inherits the container environment | Yes |
| Volume browser helper containers | Dockerode create, attach, start, wait, remove | Yes |
| Self-update and reapply helpers | Helper container mounts `/var/run/docker.sock` directly | Host Unix socket only |

The last row is the exception to everything above. See [Self-update behind a proxy](#self-update-behind-a-proxy).

The profiles below use `tecnativa/docker-socket-proxy` style flags as a worked example. Other proxies (for example `linuxserver/socket-proxy`) expose similar API-group flags; check the names against the image and version you run. Profile values are the literal strings `1` and `0`. If you run `linuxserver/socket-proxy`, add the [LinuxServer socket-proxy extras](#linuxserver-socket-proxy-extras) on top of any profile. Log viewing on that image requires `ALLOW_LOGS=1` in addition to `CONTAINERS=1`.

Compose Doctor classifies socket-proxy topologies in-app and flags risky configurations such as a proxy that allows mutating API access, publishes a host port, or joins a non-internal network. See [Compose Doctor](/features/compose-doctor).

### Monitoring / read-only profile

```env
CONTAINERS=1
IMAGES=1
NETWORKS=1
VOLUMES=1
INFO=1
SYSTEM=1
EVENTS=1
PING=1
VERSION=1
POST=0
EXEC=0
BUILD=0
SESSION=0
```

This profile supports dashboards, inventory, topology, logs, stats, Docker events, disk usage, prune previews, and read-only health and update evidence.

It does **not** support container start, stop, or restart, Compose deploy, update, or down, prune execution, volume-browser helper containers, the interactive shell, mesh network setup, auto-heal restarts, or self-update.

### Minimum management profile

```env
CONTAINERS=1
IMAGES=1
NETWORKS=1
VOLUMES=1
INFO=1
SYSTEM=1
EVENTS=1
PING=1
VERSION=1
POST=1
ALLOW_START=1
ALLOW_STOP=1
ALLOW_RESTARTS=1
EXEC=0
BUILD=0
SESSION=0
```

This profile adds everything in the monitoring profile plus image-backed Compose management, container start, stop, and restart, auto-heal restarts, pruning, mesh setup, and volume-browser helper containers.

It still does **not** support the interactive shell, build-backed stacks, or reliable self-update on a TCP-only proxy.

### Full functionality profile

```env
CONTAINERS=1
IMAGES=1
NETWORKS=1
VOLUMES=1
INFO=1
SYSTEM=1
EVENTS=1
PING=1
VERSION=1
POST=1
ALLOW_START=1
ALLOW_STOP=1
ALLOW_RESTARTS=1
EXEC=1
BUILD=1
SESSION=1
GRPC=1
```

This profile supports the full Sencho surface: the interactive shell, build-backed stacks, helper containers, cleanup actions, and self-update where a usable host Docker socket path is available to helpers (see the caveat below).

### LinuxServer socket-proxy extras

When the proxy image is `linuxserver/socket-proxy` (or `lscr.io/linuxserver/socket-proxy`), add these flags on top of **any** profile above. All three profiles include log viewing.

```env
ALLOW_LOGS=1
ALLOW_ARCHIVE=0
ALLOW_CHANGES=0
ALLOW_EXPORT=0
ALLOW_TOP=0
```

On this image, `CONTAINERS=1` lists and inspects containers; it does not open `/containers/{id}/logs`. Sencho log viewing (the Editor **View logs** modal, stack log streams, and the hub Logs tab) requires `ALLOW_LOGS=1`. Enabling `POST` does not grant log access.

If inventory still works but log viewing does not, the proxy is blocking that endpoint. A typical denial body is `403 Forbidden` with `Request forbidden by administrative rules`.

Keep the other four flags at `0`. Sencho does not use the container archive, filesystem-diff, export, or `top` Engine endpoints.

Tecnativa profiles do not use these keys. On `tecnativa/docker-socket-proxy`, logs stay under `CONTAINERS`.

### Keep these disabled

Sencho does not use these API groups today. Keep them disabled unless a future feature adds a direct dependency:

```env
AUTH=0
CONFIGS=0
SECRETS=0
SWARM=0
SERVICES=0
TASKS=0
NODES=0
PLUGINS=0
COMMIT=0
DISTRIBUTION=0
```

### Feature-to-API reference

| Capability | What the proxy must allow |
|------------|---------------------------|
| Dashboard inventory, topology, logs, stats | CONTAINERS (list, inspect, logs, stats), IMAGES, NETWORKS, VOLUMES, INFO, SYSTEM (disk usage), EVENTS, PING, VERSION. On `linuxserver/socket-proxy`, add `ALLOW_LOGS=1`. |
| Container start, stop, restart, auto-heal | CONTAINERS plus POST plus the lifecycle allow flags |
| Compose deploy, update, down, pull | POST plus the CONTAINERS, IMAGES, NETWORKS, and VOLUMES groups the Compose Engine uses through the same endpoint |
| Build-backed stacks | BUILD plus SESSION and GRPC as the BuildKit path requires |
| Interactive shell | EXEC plus CONTAINERS |
| Volume browser | CONTAINERS create, attach, start, wait, remove, IMAGES (helper image), POST; no EXEC |
| Mesh network setup | NETWORKS create, inspect, connect plus POST |
| Image update checks | IMAGES inspect plus CONTAINERS inspect |
| Prune execution | IMAGES, VOLUMES, NETWORKS, CONTAINERS prune or remove plus POST |
| Vulnerability scanning (local images) | IMAGES list and inspect (Sencho enumerates local images through the API; Trivy runs as a host binary) |
| Self-update and reapply | Helper mounts the host `/var/run/docker.sock`; a TCP-only `DOCKER_HOST` does not substitute |

### Self-update behind a proxy

Self-update and reapply helpers hardcode the host Unix socket mount into the helper container (`-v /var/run/docker.sock:/var/run/docker.sock`). They do not inherit `DOCKER_HOST`, so in a TCP-only proxy setup the day-to-day Dockerode and Compose traffic works but the update and reapply helpers will not reach the proxy.

If the deployment is behind a TCP-only proxy, use the fleet update or manual upgrade paths that fit the topology, or make sure the helpers still receive a working host socket path. The generated Pilot Agent compose also mounts the raw socket path, so a TCP-only proxy on an agent node requires editing the generated compose.

---

## Resource recommendations

| Resource | Minimum | Recommended | Notes |
|----------|---------|-------------|-------|
| CPU | 1 core | 2 cores | Compose operations spawn child processes that benefit from a second core |
| RAM | 128 MB | 256 MB | Baseline is around 100 MB at idle; increases with concurrent log streams and large fleets |
| Disk | 200 MB | 500 MB | The Docker image is around 200 MB; the database grows with metrics retention and fleet size |

Sencho itself is lightweight. The majority of resource usage on your host comes from the Docker containers it manages, not from Sencho.

---

## Networking

- **Listen port:** 1852 (fixed). Map it to any host port using Docker's `-p` flag or `ports` in your compose file
- **Inbound:** Only the listen port needs to be reachable (directly or through a reverse proxy)
- **Outbound:** No outbound connections are required for local-only setups. If you use multi-node management, Sencho needs HTTP/HTTPS access to remote Sencho instances on their configured API URLs
- **Health check:** `GET /api/health` returns `200` when the application is ready. The Docker image includes a built-in `HEALTHCHECK` that polls this endpoint every 30 seconds

---

## Environment variable checklist

Quick reference for all environment variables. See [Configuration](/getting-started/configuration) for full details.

| Variable | Default | Description |
|----------|---------|-------------|
| `COMPOSE_DIR` | `/app/compose` | Path to compose project files (1:1 rule applies) |
| `DATA_DIR` | `/app/data` | Persistent data directory |
| `FRONTEND_URL` | *(empty)* | Frontend origin for CORS; leave empty for same-origin |
| `API_RATE_LIMIT` | `200` | Maximum API requests per minute per user session (production only) |
| `API_POLLING_RATE_LIMIT` | `300` | Rate limit for dashboard polling endpoints (production only) |
| `NODE_ENV` | `production` | Set automatically in the Docker image |

SSO variables are documented separately in the [SSO Quickstart](/getting-started/sso-quickstart).
