Repository navigation
Configuration Reference
WebSSH reads configuration from environment variables. A local .env file is loaded with override=False, so variables supplied by the operating system, container runtime, or secret manager take precedence.
Start from the repository's .env.example. The tables below describe the operational contract; check that file when upgrading because new options may be added.
| Variable | Purpose | Default or requirement |
|---|---|---|
SECRET_KEY |
Encrypts and signs security-sensitive state | Required for direct production starts. The container entrypoint can generate and persist it in the data volume. |
DATA_DIR |
Canonical SQLite, key, log, and generated-secret root |
/app/data in the container; container overrides must be absolute and persistent |
DEPLOYMENT_PROFILE |
Selects deployment safeguards |
homelab; use production for Internet-facing deployments |
DEBUG |
Flask debug mode |
False; never enable in production |
HOST |
Application bind address |
127.0.0.1 outside the container |
PORT |
Application listen port | 5000 |
APPLICATION_ROOT |
URL prefix for subfolder deployments | Empty/root |
Treat SECRET_KEY as long-lived installation state. Replacing it without the supported rotation workflow invalidates encrypted SSH keys and signed state. See Backup, Restore, and Secret Rotation.
| Variable | Purpose |
|---|---|
CORS_ORIGINS |
Comma-separated Socket.IO/browser origin allowlist |
ALLOW_CORS_WILDCARD |
Allows *; homelab-only and rejected as a production origin policy |
HOST_DISCOVERY_ENABLED |
Offers manual SSH host discovery in a network range under Hosts → Find hosts |
SOCKETIO_ALLOW_ANY_ORIGIN |
With CORS_ORIGINS=*, realtime connections accept only pages served from the address used for WebSSH. true accepts any origin again; rejected in production |
TRUSTED_PROXIES |
Number of trusted reverse-proxy layers; 0 means none. Saved passwords and automation still work behind a TLS proxy at 0: a page the browser loaded over https:// from the same host counts as HTTPS. Set it to trust forwarded client addresses |
SESSION_COOKIE_SECURE |
Sends the session cookie only over HTTPS |
SESSION_TIMEOUT |
Idle SSH session timeout in seconds; default 1800
|
The production profile requires explicit HTTPS origins, secure cookies, and explicit trusted proxies. Wildcard origins are not a production configuration. See Reverse Proxy and Subfolder Deployment.
| Variable | Purpose | Default |
|---|---|---|
REGISTRATION_ENABLED |
Allows public creation of additional local accounts |
False; production rejects True
|
BOOTSTRAP_REGISTRATION_ENABLED |
Allows exactly one first administrator to be created in a fresh homelab database | Homelab default; disabled and rejected by the production profile |
ADMIN_PANEL_ENABLED |
Can disable the administrator panel | Enabled |
ADMIN_USERS |
Comma-separated existing usernames promoted at startup | Empty |
Prefer flask create-admin for controlled deployments. Public registration is independent of LDAP and OIDC sign-in.
| Variable | Purpose |
|---|---|
BLOCK_INTERNAL_SSH |
Blocks SSH connections to loopback, private, link-local, and other protected targets according to the network policy |
SSH_ALLOWED_INTERNAL_NETWORKS |
Comma-separated private networks (CIDR or single addresses) that stay reachable while BLOCK_INTERNAL_SSH=true; see below |
PROXY_JUMP_REMOTE_DNS_ALLOWLIST |
Exact hostnames a trusted bastion may resolve remotely |
SOCKS_REMOTE_DNS_ALLOWLIST |
Comma-separated exact target hostnames a trusted SOCKS5 proxy may resolve when remote_dns=true and internal targets are blocked; empty by default |
SERVER_OPERATIONS_ENABLED |
Optional authoritative environment override; otherwise Admin → Integrations controls reviewed, step-up protected service/container actions, process TERM and reboot. Product default: false
|
BLOCK_INTERNAL_SSH=true is appropriate for an Internet-facing gateway but can conflict with homelab use. Resolve and validate the exact target policy before enabling it. Host-key verification remains a separate control.
SSH_ALLOWED_INTERNAL_NETWORKS keeps BLOCK_INTERNAL_SSH=true in place and re-admits only the listed private networks, for example 10.20.0.0/16,fd00:20::/64. It applies to direct targets, Jump Hosts, SOCKS5 proxy endpoints, locally validated SOCKS destinations and file-transfer connections, and it is accepted by the production profile. Entries must lie inside 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 or fc00::/7; host bits, broader networks or any other range stop startup. Loopback, link-local (including cloud metadata addresses), multicast, reserved and IPv4-mapped IPv6 addresses always stay blocked. Every connection admitted through the list is recorded in the audit log.
SOCKS_REMOTE_DNS_ALLOWLIST is independent of the ProxyJump allowlist. Names are
canonicalized at startup; wildcards, IP literals and names with a port are
rejected. An entry permits remote resolution of that exact target name through
any saved proxy whose own endpoint passes policy. It does not permit access to
an otherwise blocked proxy endpoint. For example, an operator who trusts the
proxy at 10.20.0.10:1080 to resolve one private target could configure:
BLOCK_INTERNAL_SSH=true
SSH_ALLOWED_INTERNAL_NETWORKS=10.20.0.10/32
SOCKS_REMOTE_DNS_ALLOWLIST=db.private.exampleSave the proxy separately in the UI and enable its Resolve host names through the proxy option. WebSSH cannot validate the final IP chosen by remote DNS; the example delegates that decision to the proxy. Without an exact allowlist match, names are resolved and policy-checked locally instead. IP-literal targets always follow address policy. See the complete SOCKS5 routing table.
Loopback proxies require BLOCK_INTERNAL_SSH=false and cannot be re-admitted
through SSH_ALLOWED_INTERNAL_NETWORKS in production. No ALL_PROXY or other
global proxy environment setting selects a route; hosts reference saved proxies.
| Variable | Default | Contract |
|---|---|---|
GUNICORN_THREADS |
64 |
Supported range 8 to 256
|
MAX_SOCKET_CONNECTIONS |
48 |
Global admitted Socket.IO connections |
MAX_SOCKET_CONNECTIONS_PER_USER |
8 |
Per-user Socket.IO connections |
BACKGROUND_WORKERS |
Calculated baseline | Bounded executor; minimum is cleanup jobs plus global SSH-session and background-job quotas. A lower explicit value is raised to the minimum with a startup warning |
RUNTIME_SHUTDOWN_GRACE_SECONDS |
5 |
Graceful shutdown window; supported range 1 to 30 seconds |
Production requires exactly one Gunicorn worker with gthread. Live SSH channels and part of the coordination state are process-local. Increasing worker count does not scale WebSSH safely.
Keep at least four HTTP threads free:
GUNICORN_THREADS - MAX_SOCKET_CONNECTIONS >= 4
LDAP adds a bounded cleanup task to the same runtime lifecycle; size background capacity as one combined budget.
| Variable | Default |
|---|---|
QUOTA_SSH_SESSION_GLOBAL |
10 |
QUOTA_SSH_SESSION_PER_USER |
5 |
QUOTA_QUICK_CONNECTION_GLOBAL |
12 |
QUOTA_QUICK_CONNECTION_PER_USER |
3 |
QUOTA_TRANSFER_GLOBAL |
8 |
QUOTA_TRANSFER_PER_USER |
2 |
QUOTA_TEMP_BYTES_GLOBAL |
1073741824 |
QUOTA_TEMP_BYTES_PER_USER |
536870912 |
QUOTA_BACKGROUND_JOB_GLOBAL |
4 |
QUOTA_BACKGROUND_JOB_PER_USER |
1 |
Connection, transfer, background-work, and thread limits form one capacity model. Do not raise one limit in isolation without checking HTTP reserve, memory, remote-server capacity, and shutdown behavior.
| Variable | Default |
|---|---|
RATELIMIT_ENABLED |
true |
RATELIMIT_STORAGE_URL |
memory:// |
RATELIMIT_DEFAULT |
200 per hour |
RATELIMIT_LOGIN_LIMIT |
5 per minute |
RATELIMIT_REAUTH |
5 per minute |
SSH_CONNECT_RATELIMIT |
10 per minute |
SSH_KEY_WRITE_RATELIMIT |
30 per minute |
SSH_KEY_LIST_RATELIMIT |
30 per minute |
CONNECTION_MUTATION_RATELIMIT |
60 per minute |
Key listings and the refresh after renaming, replacing, or deleting a key share
SSH_KEY_LIST_RATELIMIT per user across browser connections. Admission is checked
before the mutation; when exhausted, the change is rejected without altering the
key. Accepted changes retain their acknowledgement and updated key list. Usability
is still checked against the current key files, without caching decrypted keys.
| Variable | Default |
|---|---|
SSH_KEY_MAX_RECORDS |
100 |
SSH_KEY_STORE_MAX_BYTES |
8388608 (8 MiB encrypted) |
SSH_OUTPUT_MAX_UNACKED_BYTES_PER_SOCKET |
524288 (512 KiB) |
SSH_OUTPUT_MAX_UNACKED_EVENTS_PER_SOCKET |
128 |
SSH_OUTPUT_MAX_UNACKED_EVENTS_PER_USER |
1024 |
SSH_OUTPUT_MAX_UNACKED_EVENTS_GLOBAL |
8192 |
SSH_OUTPUT_MAX_UNACKED_BYTES_PER_USER |
4194304 (4 MiB) |
SSH_OUTPUT_MAX_UNACKED_BYTES_GLOBAL |
33554432 (32 MiB) |
SSH_OUTPUT_ACK_TIMEOUT_SECONDS |
10 seconds |
The key limits reject only storage growth. A pre-existing store above the byte limit remains readable and can be renamed, deleted, or replaced with smaller keys. Live terminal output is acknowledged by each browser. If one browser keeps an acknowledgement budget blocked for the configured timeout, WebSSH applies SSH backpressure and then disconnects only that browser; the underlying SSH or persistent tmux session remains available for reconnect.
All terminals in one browser connection share a delivery window of at most eight unacknowledged output events, further constrained by the budgets above. This paces SSH readers and keeps bursts of output ACKs below Engine.IO's polling packet limit, with room for control traffic. Waiting for a delivery slot does not itself mean that an ACK is overdue. The configured timeout still applies to each outstanding output event. Opening more terminal tabs does not create additional Socket.IO connections.
memory:// is process-local and counters reset when the process restarts. Use a redis:// URL for durable, shared counters. Redis does not change the one-worker architecture.
| Variable | Default |
|---|---|
MAX_DOWNLOAD_SIZE |
104857600 (100 MiB) |
MAX_ZIP_DOWNLOAD_SIZE |
524288000 (500 MiB) |
MAX_TRANSFER_MEMBERS |
10000 |
MAX_PREVIEW_SIZE |
512000 bytes |
MAX_PREVIEW_TAIL_LINES |
10000 |
MAX_SUPPORTED_FILE_SIZE |
1073741824 (1 GiB) |
SFTP_OPERATION_TIMEOUT |
30 seconds |
SFTP_CHMOD_ENABLED |
Optional authoritative override; otherwise Admin → Integrations controls SFTP Unix permissions (product default false), requiring lsetstat@openssh.com v1; separate file/folder modes, optional recursive preview jobs with bounded resource use; SFTP only |
SFTP_MAX_PACKET_BYTES |
1048576 (1 MiB) |
SFTP_MAX_HANDLE_BYTES |
16384 (16 KiB) |
MAX_EDITOR_FILE_SIZE |
5242880 (5 MiB) |
EDITOR_SAVE_BYTES_PER_MINUTE |
20971520 (20 MiB per user) |
TRANSFER_TEMP_DIR |
DATA_DIR/tmp |
FILE_CONTROL_MAX_PATH_BYTES |
4096 bytes |
FILE_CONTROL_BYTES_PER_MINUTE |
2097152 (2 MiB per user) |
REMOTE_FILENAME_MAX_BYTES |
4096 bytes |
REMOTE_LISTING_MAX_METADATA_BYTES |
4194304 (4 MiB) |
REMOTE_LISTING_PAGE_SIZE |
500 entries |
REMOTE_LISTING_SNAPSHOT_TTL_SECONDS |
60 seconds |
REMOTE_LISTING_SNAPSHOT_MAX_STATES |
8 snapshots per process |
REMOTE_LISTING_SNAPSHOT_MAX_PER_USER |
4 snapshots per user |
CONNECTION_STORE_RECOVERY_MAX_BYTES |
16777216 (16 MiB) |
CONNECTION_STORE_RECOVERY_MAX_RECORDS |
10000 |
Bulk uploads and downloads are streamed over HTTP; Socket.IO carries control events and bounded editor content rather than entire files. Align proxy request-body and timeout limits with WebSSH when increasing an application limit.
Enable file permission changes under Settings → Administration → Integrations.
SFTP_CHMOD_ENABLED is an optional authoritative environment override, not a
required activation step. See Unix permissions.
SFTP directory responses are paged. Declared protocol packet and opaque handle
size, raw entry count (including . and ..), filename, longname, aggregate
metadata, and file-control budgets are enforced before data is retained or
reflected.
| Variable | Default |
|---|---|
PROFILE_MAX_RECORDS |
500 |
JUMP_HOST_MAX_RECORDS |
100 |
SOCKS_PROXY_MAX_RECORDS |
100 (supported range 10–1000) |
CONNECTION_STORE_MAX_BYTES |
2097152 (2 MiB per store) |
CONNECTION_CONFIG_MAX_BYTES |
4194304 (4 MiB combined) |
The per-store byte limit applies separately to profiles.json, jump_hosts.json
and socks_proxies.json. The combined limit counts all three files for one user;
it is not a separate allowance for each route type. The SOCKS record limit also
contributes to the minimum permitted CONNECTION_STORE_RECOVERY_MAX_RECORDS.
Legacy stores above a normal limit are quarantined from the browser UI. After
stopping every WebSSH process, flask --app start connection-store list and
connection-store delete provide a non-secret, bounded recovery path up to
the separate recovery ceilings shown above. Growth is rejected, and profile
and route/key references remain ownership checked. Recovery accepts
--kind profiles, --kind jump-hosts or --kind socks-proxies; route deletion remains
blocked while profiles reference the record.
| Variable | Default or purpose |
|---|---|
TMUX_ENABLED |
Optional override of Admin → Integrations; product default true (tmux available) |
TMUX_DEFAULT |
Optional override of Admin → Integrations; product default false (not preselected) |
TMUX_SESSION_PREFIX |
webssh |
HOST_KEY_MANAGEMENT_ENABLED |
true |
RECOVERY_CODES_ENABLED |
true |
AUDIT_EXPORT_ENABLED |
true |
MAX_RECOVERY_JSON_SIZE |
4096 bytes |
| Variable | Purpose |
|---|---|
WEBAUTHN_ENABLED |
Enables Passkey registration and login |
WEBAUTHN_RP_ID |
Public domain only; defaults to localhost
|
WEBAUTHN_RP_NAME |
Display name; defaults to WebSSH
|
WEBAUTHN_ORIGIN |
Exact browser origin, including scheme and optional port |
MAX_WEBAUTHN_JSON_SIZE |
Request ceiling; defaults to 64 KiB |
| Variable | Default |
|---|---|
AUDIT_LOG_MAX_BYTES |
10485760 |
AUDIT_LOG_BACKUP_COUNT |
5 |
BACKUP_UPLOAD_MAX_SIZE |
1073741824 |
BACKUP_OPERATION_TIMEOUT |
1800 |
BACKUP_DOWNLOAD_TTL |
600 |
BACKUP_MAX_MEMBERS |
10000 |
BACKUP_MAX_FILE_SIZE |
1073741824 |
BACKUP_MAX_TOTAL_SIZE |
10737418240 |
BACKUP_MAX_COMPRESSION_RATIO |
200 |
BACKUP_MAX_MANIFEST_SIZE |
10485760 |
BACKUP_TEMP_DIR |
System temporary directory under webssh-backup-operations
|
BACKUP_RECOVERY_DURABLE |
false; must be true with durable external storage for online restore |
Audit export scans at most 50,000 records and declares truncation in response metadata. Backup safety limits also cap archive member count, individual size, total size, compression ratio, and manifest size.
BACKUP_TEMP_DIR may remain ephemeral for backup creation. Online restore
additionally requires it to be absolute, private, outside DATA_DIR, and
durable across process/container replacement.
Identity-provider variables are grouped in their dedicated pages:
- LDAP and Active Directory
- OpenID Connect
- GitHub Authentication
- Passkeys and Recovery Codes
- Tailscale SSH
All optional identity providers are disabled until explicitly configured. GitHub is the exception to the environment-variable model: its complete configuration and encrypted client secret are managed in the Admin Panel, so no Compose or .env entries are required. Do not place bind passwords, OIDC client secrets, or other reusable credentials directly in Compose YAML committed to source control.
Render the merged Compose configuration before deployment:
docker compose \
-f docker-compose.yml \
-f docker-compose.production.yml \
configAdd -f docker-compose.ldap.yml when LDAP is enabled. Confirm the effective bind address, origins, cookie mode, registration state, proxy trust, volumes, and secret files before starting the service.
Manage file permission changes, server operations, tmux availability and the initial tmux selection under Settings → Administration → Integrations. Sensitive changes require administrator reauthentication and are audited.
For SFTP_CHMOD_ENABLED, SERVER_OPERATIONS_ENABLED, TMUX_ENABLED and
TMUX_DEFAULT, priority is: explicitly supplied environment value, saved Admin
setting, product default. Both explicit true and explicit false lock the UI;
an empty value counts as not supplied.
Compose/environment includes variables passed by Docker, systemd, a shell or the
application's .env; the application cannot identify which file supplied them.
Settings shows the key, effective value, stored value and source. Edit or remove
the variable and recreate the container to release the lock. Existing sessions
remain open; reload clients and reconnect SFTP after changing capabilities.
Until an administrator changes a switch, its stored value follows the last
explicit environment value, so removing the variable keeps the behavior the
operator set last. An administrator's own choice is retained under an override
and becomes effective again when that override is removed. Without the variable,
an administrator can enable the feature in the web interface; set it explicitly
to false to keep it off. Older Compose files do not need replacing.
Server actions and chmod are off by default; tmux is available by default and
never preselected, matching every Compose file shipped before 3.0.
Disabling server operations prevents new actions without undoing commands already
sent to a server. Remote account privileges are never expanded by these switches.
AUTOMATION_ENABLED follows the same rule. REGISTRATION_ENABLED remains only
the initial value, as in 2.x: a choice saved in Administration wins, and the
production profile always prohibits public self-registration. Password storage
remains an Admin policy. Authentication features retain their separate deployment-capability,
readiness and Admin-activation checks: allowing a capability does not enroll users
or bypass provider, HTTPS, origin, RP-ID or recovery checks. Networking, identity
provider trust, secrets, resource ceilings, mounts and backup recovery paths remain
deployment configuration. Audit retention is already managed in Administration.
Signed-in users see links to support WebSSH development in the account menu and the Settings side panel. Project support links in Administration → Integrations hides them for all users; the links load nothing from external sites until a user follows one.
TOTP capability is available by default in 3.0; Admin activation remains off
until explicitly enabled. TOTP_ENABLED=false is the optional operator kill
switch. Existing enrolled users and saved activation choices are retained.
Updating an existing deployment's image through latest does not replace its
Compose file. Keep that file and its custom values. With an old Compose that
sets TMUX_ENABLED=true, version 3.0 continues to use true. Administration
shows Compose/environment as the authoritative source, the exact variable,
the effective value and the saved Admin value. The switch remains locked while
the override exists. The first 3.0 start saves the supplied value if no Admin
value exists; subsequent image updates do not replace saved Admin choices.
This behavior also applies to TMUX_DEFAULT, file permissions and server actions.
The current example Compose is for new installations. It leaves the runtime feature overrides commented out so administrators can manage them immediately in Administration → Integrations. Product defaults apply until an Admin value is saved: tmux available but not preselected; file permissions and server operations off. The defaults match the previous example Compose, so replacing an old Compose file does not change tmux behavior. See the upgrade table in the WebSSH 3 notes for all upgrade paths. Network, authentication and other advanced deployment examples remain present.
Moving an existing deployment to Admin-managed settings is optional: start 3.0 with its original Compose, inspect the saved values, then remove only the corresponding environment entries and recreate the container. The saved values become effective. The rest of the deployment file stays intact.
Do not replace a customized old Compose with a new-installation template during an image upgrade. Removed historical environment values cannot be reconstructed if version 3.0 has never seen them. If a saved Admin value differs from the current override, inspect it before removing the override.
WebSSH documentation - Project - Issues - Discussions - Security
This Wiki is published from the versioned docs/wiki source. Please propose documentation changes through a pull request.
WebSSH Wiki
Install and operate
- Installation from Source
- Docker and Docker Compose
- Production Deployment
- Reverse Proxy and Subfolder Deployment
- Upgrading, Rollback, and FAQ
Identity and access
- Users and Account Management
- Authentication Overview
- GitHub Authentication
- LDAP and Active Directory
- OpenID Connect
- Passkeys and Recovery Codes
SSH and files
- SSH Connections and Host Keys
- Profiles, Jump Hosts, and Commands
- Terminal and Persistent tmux Sessions
- SFTP File Workspace and Transfers
- Tailscale SSH
Reference and administration