mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
9eb945a6f0
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.
274 lines
9.7 KiB
Plaintext
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.
|