Files
sencho/docs/getting-started/configuration.mdx
T
Anso 9eb945a6f0 fix: run as root by default to eliminate stack-folder permission failures (#501)
Every filesystem operation against user compose folders (save, create,
deploy, update, rollback, template install, fleet snapshot restore)
previously failed with EACCES whenever a stack container had chowned
its own bind mount to another UID, which is extremely common with
linuxserver/* images and anything that runs as root by default.

Running Sencho as root eliminates the entire class of permission bugs
at the source and matches the default posture of Portainer, Dockge,
Komodo, and Yacht. Mounting /var/run/docker.sock is already equivalent
to root-on-host, so the previous non-root hardening provided essentially
no additional isolation while breaking real features.

Changes:

- docker-entrypoint.sh: default path stays root, no GID dance, no
  privilege drop. Opt-out via SENCHO_USER=sencho restores the legacy
  behavior bit-for-bit (chown data dir, match Docker socket GID,
  su-exec to the user). Fails fast if SENCHO_USER names a nonexistent
  account. Kubernetes / OpenShift forced-non-root compat preserved via
  the existing id -u = 0 guard.
- FileSystemService: delete forceDeleteViaDocker (the ~40-line helper
  that shelled out to an alpine container to work around EACCES during
  deleteStack) and simplify deleteStack to a single fsPromises.rm call.
  Tests updated accordingly.
- Dockerfile: keep the sencho user+group pre-created so the opt-out
  path works out of the box; comments updated to document the new
  default.
- Docs: new "Container user" section in configuration.mdx documenting
  the root default and the SENCHO_USER opt-out; troubleshooting and
  self-hosting updated to match.
2026-04-10 21:35:31 -04:00

274 lines
9.7 KiB
Plaintext

---
title: Configuration
description: Environment variables, volume mounts, and the 1:1 path rule.
---
Sencho is configured entirely through environment variables and Docker volume mounts. There is no config file to edit inside the container.
## Required environment variables
| Variable | Description |
|----------|-------------|
| `COMPOSE_DIR` | Absolute path to the directory that contains your Compose stacks. Every subdirectory inside becomes a stack in Sencho. |
<Note>
**JWT_SECRET is generated automatically.** Sencho creates a secure random signing key during initial setup and stores it in its database. You do not need to provide one.
</Note>
### How Sencho organizes your compose directory
When you point `COMPOSE_DIR` at a directory, Sencho expects each stack to live in its own subdirectory. If you create a stack through the UI, Sencho automatically creates a subfolder and places a blank `compose.yaml` inside it. Sencho does not move or "capture" existing files - it simply treats every subdirectory as a separate stack.
## Optional environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `3000` | Port the Sencho HTTP server listens on. |
| `DATA_DIR` | `/app/data` | Directory where Sencho stores its SQLite database, node registry, and cached metrics. |
| `FRONTEND_URL` | *(empty)* | Frontend origin for CORS. Only needed if the UI is served from a different domain than the API. Leave empty for same-origin setups. |
| `NODE_ENV` | `production` | Set automatically in the Docker image. Only change this for local development. |
| `SENCHO_USER` | *(unset)* | Optional. When set to a username present inside the container (`sencho` is pre-created for this purpose), the entrypoint drops privileges to that user at startup instead of running as `root`. See [Running as a non-root user](#running-as-a-non-root-user) below. |
## Container user
Sencho runs as `root` inside the container by default, matching Portainer, Dockge, Komodo, and Yacht. This is required so Sencho can always write to your compose folders, even when a stack container (commonly anything from `linuxserver/*`) has chowned its own bind mount to another UID. Mounting `/var/run/docker.sock` already grants root-equivalent access to the host, so running the Sencho process itself as root does not change the effective privilege boundary.
**What this means for you:**
- Files Sencho creates inside your compose directory (new stacks, edited `compose.yaml`, generated `.env` files) will be owned by `root` on the host. If you edit those files outside Sencho using your own editor, you will need `sudo` or a one-time `chown`.
- The `/app/data` directory is internal to Sencho; its ownership does not matter for host-side tooling.
### Running as a non-root user
If organisational policy, compliance scanners, or a rootless Docker setup requires the container to drop privileges, set `SENCHO_USER=sencho` in the environment. The entrypoint will chown `/app/data` to the sencho user, match the host's Docker socket group, and exec Node under that user.
```yaml
services:
sencho:
image: saelix/sencho:latest
environment:
- COMPOSE_DIR=/opt/compose
- SENCHO_USER=sencho # opt into non-root mode
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/compose:/opt/compose
- ./sencho-data:/app/data
```
<Warning>
Non-root mode can cause filesystem write failures for any stack whose compose folder is owned by a UID other than `sencho`. If you hit "Failed to save stack" or similar errors after opting in, either `chown` the affected stack folder to the sencho user inside the container, or unset `SENCHO_USER` to return to the default (root) mode.
</Warning>
## SSO environment variables
If you use SSO (Admiral), configure your identity providers via environment variables:
| Variable | Description |
|----------|-------------|
| `SSO_LDAP_ENABLED` | Enable LDAP/AD authentication |
| `SSO_OIDC_GOOGLE_ENABLED` | Enable Google SSO |
| `SSO_OIDC_GITHUB_ENABLED` | Enable GitHub SSO |
| `SSO_OIDC_OKTA_ENABLED` | Enable Okta SSO |
| `SSO_CALLBACK_URL` | External base URL for OAuth callbacks (required behind reverse proxy) |
For the full SSO configuration reference and setup guides, see [SSO Authentication →](/features/sso).
## Required volume mounts
### Docker socket
Sencho needs access to the Docker daemon to manage containers:
```yaml
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
### Data directory
Sencho's database persists all your settings, nodes, alerts, and metrics history. Mount a named volume or host path so it survives container restarts:
```yaml
volumes:
- ./sencho-data:/app/data
```
<Warning>
Without a persistent data mount, Sencho will lose all configuration - including registered nodes, alerts, and settings - every time the container restarts.
</Warning>
### Compose directory - the 1:1 path rule
<Warning>
This is the most common source of deployment problems. Read carefully.
</Warning>
When Sencho runs `docker compose up`, it does so on your **host machine**. Docker resolves relative volume paths in your Compose files relative to the **host** path of the stack directory - not the path inside the Sencho container.
**The rule:** Mount your Compose directory at the **exact same path** inside the container as it exists on your host.
```yaml
# ✅ Correct - host path matches container path
volumes:
- /home/user/docker:/home/user/docker
environment:
- COMPOSE_DIR=/home/user/docker
```
```yaml
# ❌ Wrong - paths differ, relative volumes will break
volumes:
- /home/user/docker:/app/compose
environment:
- COMPOSE_DIR=/app/compose
```
If you use a simple path like `/opt/compose` on your host, mount it at `/opt/compose` in the container:
```yaml
volumes:
- /opt/compose:/opt/compose
environment:
- COMPOSE_DIR=/opt/compose
```
## Full docker-compose.yml example
```yaml
services:
sencho:
image: saelix/sencho:latest
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./sencho-data:/app/data
- /opt/compose:/opt/compose # 1:1 path rule
environment:
- COMPOSE_DIR=/opt/compose
- DATA_DIR=/app/data
```
## Optional: global environment file
If your Compose stacks share common variables (e.g. `PUID`, `PGID`, `TZ`), you can pass an `env_file` to the Sencho container so those variables are available in the host environment when `docker compose` runs:
```yaml
services:
sencho:
image: saelix/sencho:latest
env_file:
- /opt/compose/globals.env # shared vars for all stacks
environment:
- COMPOSE_DIR=/opt/compose
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/compose:/opt/compose
- ./sencho-data:/app/data
```
## Reverse proxy setup
Sencho works behind any reverse proxy. The only requirement is that WebSocket connections are forwarded correctly (used for live logs, container terminals, and the host console).
### Nginx
```nginx
server {
listen 80;
server_name sencho.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
}
}
```
### Nginx with SSL (Let's Encrypt)
```nginx
server {
listen 80;
server_name sencho.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name sencho.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/sencho.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sencho.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}
```
Use [Certbot](https://certbot.eff.org/) to obtain and auto-renew certificates: `certbot --nginx -d sencho.yourdomain.com`.
### Traefik (Docker labels)
```yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.sencho.rule=Host(`sencho.yourdomain.com`)"
- "traefik.http.services.sencho.loadbalancer.server.port=3000"
```
### Traefik with SSL (Let's Encrypt)
```yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.sencho.rule=Host(`sencho.yourdomain.com`)"
- "traefik.http.routers.sencho.entrypoints=websecure"
- "traefik.http.routers.sencho.tls.certresolver=letsencrypt"
- "traefik.http.services.sencho.loadbalancer.server.port=3000"
# HTTP to HTTPS redirect
- "traefik.http.routers.sencho-http.rule=Host(`sencho.yourdomain.com`)"
- "traefik.http.routers.sencho-http.entrypoints=web"
- "traefik.http.routers.sencho-http.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
```
<Note>
Traefik handles WebSocket upgrades automatically for HTTP/1.1 backends. No extra configuration needed.
</Note>
### Caddy
```
sencho.yourdomain.com {
reverse_proxy localhost:3000
}
```
Caddy automatically obtains and renews SSL certificates via Let's Encrypt. WebSocket connections are forwarded without additional configuration.
## First boot
After starting Sencho, open it in your browser. If no admin account exists yet, you'll be taken to a setup screen to create one. This only appears once - subsequent visits go directly to the login page.