TL;DR
The self-hosting setup currently suffers from three silent security and functional bugs:
- Public/Literal Secrets: Setup instructions present OpenSSL commands as raw .env values, leading users to save publicly known command strings as live secrets.
- Invalid Encryption Key Format: The documented ENCRYPTION_KEY command produces an 88-character Base64 string, but the backend requires a 64-character hex string, causing features like proxy and API key storage to crash at runtime.
- Secret Truncation via $: .envexample contains unescaped $ characters, causing Docker Compose to treat them as unset variables and silently truncate keys/passwords which could lead to things to crash.
Description
Following the documented setup in docs/self-hosting-docker.md (or copying .envexample directly) can result in broken functionality or compromised security. There are three interrelated issues where setup succeeds silently, but leaves the instance insecure or partially broken. The third bug is the one that led to the other 2 to be found.
Environment Tested:
- Branch/Commit:
master
- OS / Runtime: Windows / Docker Compose (Docker 28.4.0)
1. Documentation Presents Commands as .env Values (docs/self-hosting-docker.md)
Issue:
Step 4 of docs/self-hosting-docker.md instructs users to "Create an environment file to save your variables nano .env with the following contents:" and lists a block of key-value pairs where the values are OpenSSL commands:
JWT_SECRET=openssl rand -base64 48
DB_PASSWORD=openssl rand -base64 24
ENCRYPTION_KEY=openssl rand -base64 64
SESSION_SECRET=openssl rand -base64 48
MINIO_SECRET_KEY=openssl rand -base64 24
While most people can infer that these are terminal commands to generate random strings, users who are new to Docker or .env files (or those who simply copy-paste the block without reading closely) will end up with the literal command strings saved inside their .env file.
Impact:
Because the file is copied as-is, the application sets its active secrets to the literal string "openssl rand -base64 48". Every instance configured this way shares the exact same publicly documented keys, allowing anyone to forge valid JWT tokens, hijack sessions, or decrypt sensitive stored data (like LLM API keys and proxy passwords).
Additionally, the containers start and function normally without raising any errors, leaving the instance silently exposed.
To Reproduce:
- Copy the example block directly into a
.env file.
- Run
docker compose up -d.
- Check the container's environment variables:
docker compose exec backend printenv JWT_SECRET
# Output: openssl rand -base64 48
2. Documented ENCRYPTION_KEY Command Generates Invalid Format
Issue:
The guide recommends running openssl rand -base64 64 to generate the ENCRYPTION_KEY. However, the application validation logic (server/src/utils/auth.ts) strictly requires a 64-character hex string:
const HEX_64_RE = /^[0-9a-fA-F]{64}$/;
The recommended Base64 command generates 88 characters wrapped across two lines, which fails regex validation during runtime tasks like saving proxy configurations.
Impact:
Saving proxy settings, robot credentials, or LLM API keys fails at runtime without throwing a clear setup error during initial container startup.
To Reproduce:
- Set
ENCRYPTION_KEY to the output of openssl rand -base64 64
- Restart the backend:
docker compose up -d
- Log in and save any proxy URL(https://www.google.com/) under proxy settings (in localhost)
- Look at the result of docker after running
docker compose logs backend --tail 1
Error Output:
on localhost/cloud: 
On terminal:
backend-1 | Could not save proxy configuration - Error: ENCRYPTION_KEY is missing or invalid. Set a 64-character hex string in your .env file.
3. Docker Compose Silently Truncates Secrets Containing $ (ENVEXAMPLE)
Most programmers who have some experience with .env will just copy ENVEXAMPLE as is and follow the readme to set up docker and may not read the docs since the example env is already given.
Issue:
Line 3 of ENVEXAMPLE includes an unescaped dollar sign in the default secret:
JWT_SECRET=a9Z$kLq7^f03GzNw!bP9dH4xV6sT2yXl3O8vR@uYq3
Docker Compose interprets $kLq7 as an environment variable reference since it starts with $. Since kLq7 is unset, Docker Compose evaluates it to an empty string, silently dropping characters before injecting the variable into the container.
Impact:
- Running Compose yields warnings:
The "kLq7" variable is not set. Defaulting to a blank string.
- The container receives a truncated 37-character secret instead of the intended 42-character value.
- Any custom password or secret created by a user that includes an unescaped
$ (e.g., in DB_PASSWORD or MINIO_SECRET_KEY) will be altered silently.
To Reproduce:
- Set
JWT_SECRET=test$FOO in .env.
- Execute
docker compose up -d backend.
- Check the received variable:
docker compose exec backend printenv JWT_SECRET
# Output: test
I am happy to open a PR with these fixes if this sounds good!
TL;DR
The self-hosting setup currently suffers from three silent security and functional bugs:
Description
Following the documented setup in
docs/self-hosting-docker.md(or copying.envexampledirectly) can result in broken functionality or compromised security. There are three interrelated issues where setup succeeds silently, but leaves the instance insecure or partially broken. The third bug is the one that led to the other 2 to be found.Environment Tested:
master1. Documentation Presents Commands as
.envValues (docs/self-hosting-docker.md)Issue:
Step 4 of
docs/self-hosting-docker.mdinstructs users to "Create an environment file to save your variablesnano .envwith the following contents:" and lists a block of key-value pairs where the values are OpenSSL commands:While most people can infer that these are terminal commands to generate random strings, users who are new to Docker or
.envfiles (or those who simply copy-paste the block without reading closely) will end up with the literal command strings saved inside their.envfile.Impact:
Because the file is copied as-is, the application sets its active secrets to the literal string
"openssl rand -base64 48". Every instance configured this way shares the exact same publicly documented keys, allowing anyone to forge valid JWT tokens, hijack sessions, or decrypt sensitive stored data (like LLM API keys and proxy passwords).Additionally, the containers start and function normally without raising any errors, leaving the instance silently exposed.
To Reproduce:
.envfile.docker compose up -d.2. Documented
ENCRYPTION_KEYCommand Generates Invalid FormatIssue:
The guide recommends running
openssl rand -base64 64to generate theENCRYPTION_KEY. However, the application validation logic (server/src/utils/auth.ts) strictly requires a 64-character hex string:The recommended Base64 command generates 88 characters wrapped across two lines, which fails regex validation during runtime tasks like saving proxy configurations.
Impact:
Saving proxy settings, robot credentials, or LLM API keys fails at runtime without throwing a clear setup error during initial container startup.
To Reproduce:
ENCRYPTION_KEYto the output ofopenssl rand -base64 64docker compose up -ddocker compose logs backend --tail 1Error Output:
on localhost/cloud:
On terminal:
3. Docker Compose Silently Truncates Secrets Containing
$(ENVEXAMPLE)Most programmers who have some experience with .env will just copy ENVEXAMPLE as is and follow the readme to set up docker and may not read the docs since the example env is already given.
Issue:
Line 3 of
ENVEXAMPLEincludes an unescaped dollar sign in the default secret:Docker Compose interprets
$kLq7as an environment variable reference since it starts with $. SincekLq7is unset, Docker Compose evaluates it to an empty string, silently dropping characters before injecting the variable into the container.Impact:
The "kLq7" variable is not set. Defaulting to a blank string.$(e.g., inDB_PASSWORDorMINIO_SECRET_KEY) will be altered silently.To Reproduce:
JWT_SECRET=test$FOOin.env.docker compose up -d backend.I am happy to open a PR with these fixes if this sounds good!