Files
sencho/docs/operations/self-hosting.mdx
T
Anso 8e1b9826cf fix(api): add tiered rate limiting to prevent polling lockouts (#460)
* fix(api): add tiered rate limiting to prevent polling lockouts

Replace the single global rate limiter (100 req/min/IP) with a tiered
system that separates high-frequency polling endpoints from standard
API traffic:

- Polling tier (300/min): /stats, /system/stats, /stacks/statuses,
  /metrics/historical, /health, /meta, /auth/status, /auth/sso/providers,
  /license. Exempt from the global limiter but governed by their own
  safety net to prevent resource exhaustion.
- Standard tier (200/min): All other endpoints, raised from 100.
- Webhook tier (500/min): POST /webhooks/:id/trigger, dedicated limiter
  for CI/CD platforms sharing datacenter IPs.
- Auth tier: Unchanged (5-10 attempts / 15 min).

Enterprise adaptations:
- Authenticated requests keyed by user session (JWT sub/username) instead
  of IP, preventing shared NAT/VPN environments from pooling budgets.
- Internal node-to-node traffic (node_proxy tokens) bypasses all rate
  limiters entirely.

Includes comprehensive stress tests (21 cases) validating tier
separation, node proxy bypass, and per-user keying.

* chore(deps): bump axios to 1.15.0 to fix SSRF vulnerability

Addresses GHSA-3p68-rc4w-qgx5 (NO_PROXY hostname normalization bypass).
2026-04-09 17:56:58 -04:00

97 lines
4.5 KiB
Plaintext

---
title: Self-Hosting Best Practices
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.
Sencho mitigates this with privilege dropping:
1. The container starts as root to fix volume ownership and resolve Docker socket group permissions
2. The entrypoint script then drops to a non-root `sencho` user
3. All application code runs as the `sencho` user
If your environment requires stricter isolation, consider:
- Running Sencho on a dedicated Docker host
- Using Docker's `--userns-remap` for user namespace isolation
- Placing Sencho behind a reverse proxy with authentication (see [Configuration](/getting-started/configuration#reverse-proxy-setup))
---
## 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:** 3000 (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).