- **New Features** - Added automatic `.env` loading for secrets and environment variables, with custom file paths and existing-environment precedence. - Added a `--env-file` option and a sample environment configuration template. - Increased Docker file descriptor limits for improved reliability under high connection counts. - **Bug Fixes** - Fixed title parsing for multi-byte UTF-8 characters, preventing crashes and incorrect volume extraction. - **Documentation** - Added guidance for `.env` configuration, Docker usage, restart requirements, and resolving file descriptor exhaustion.
12 KiB
🔐 Secrets & Environment Variables
Every deployment needs credentials: provider logins, output-user passwords, webhook tokens, web server secrets. Tuliprox keeps the repository free of real credentials while making it easy to inject them at runtime.
Host-agnostic. These instructions deliberately do not assume any particular host, container platform, or cloud provider. Secrets are read from the process environment and from files we generate on the machine itself, so the same workflow works whether you run a container, a service manager, a process supervisor, a small server, or a hosted secret store of your choice. The mechanism to supply an environment variable is always the same: it must be present in the environment of the
tuliproxprocess at startup.
What counts as a secret
| File | Field | Example env var |
|---|---|---|
source.yml |
input url, username, password |
CLOUDTV_1_URL, CLOUDTV_1_USER, CLOUDTV_1_PASS |
source.yml |
input epg.sources[].url |
CLOUDTV_1_EPG_URL |
source.yml |
input panel_api.api_key (account management) |
PROVIDER_PANEL_API_KEY |
api-proxy.yml |
output user password / token for every published user |
XTR_USER_LOCAL_PASS, XTR_USER_LOCAL_TOKEN |
config.yml |
web_ui.auth.secret (JWT signing, 64-hex) |
TULIPROX_WEB_SECRET |
config.yml |
messaging webhooks/tokens: Telegram bot token, Discord URL, Pushover token/key, ntfy/Gotify tokens, Slack URL, generic REST URL + signing_secret + Authorization headers |
TULIPROX_DISCORD_WEBHOOK, TULIPROX_TELEGRAM_TOKEN, TULIPROX_SIGNING_SECRET |
config.yml |
metadata_update.tmdb.api_key |
TULIPROX_TMDB_API_KEY |
config.yml |
reverse_proxy.rewrite_secret |
TULIPROX_PROXY_REWRITE_SECRET |
config/user.txt |
Web UI Argon2 password hashes (see below — not env-injectable) | file only |
Anything a provider, a player, a notification bot, or a browser authenticates with is a secret and must not be in git.
${env:VAR} interpolation
Every config file that Tuliprox reads (config.yml, source.yml, api-proxy.yml, mapping.yml, template.yml)
supports environment-variable interpolation with the syntax:
${env:VAR_NAME}
Variable names match [a-zA-Z_][a-zA-Z0-9_]*.
-
How it works: the placeholder is resolved before the file is parsed as YAML, from the environment of the running process. If the variable is missing, Tuliprox logs
Could not resolve env var 'VAR_NAME'and leaves the literal${env:VAR_NAME}in place so the problem is visible. -
Quoting: keep string values quoted, exactly like the value the variable will expand to:
inputs: - name: provider type: xtream url: "${env:CLOUDTV_URL}" username: "${env:CLOUDTV_USER}" password: "${env:CLOUDTV_PASS}"This stays valid YAML before and after substitution.
-
Use only for string fields. Numbers (
exp_date, ports,token_ttl_mins) and booleans (enabled) still belong directly in the file; injecting them through env vars is fragile. -
Not supported in
user.txt. Web UI credential files are read directly and must be written on the machine (see below). -
Also works in CLI paths: any
--home/-c/-i/-aargument can be${env:...}too.
.env file support
Tuliprox automatically loads environment variables from a .env file at startup. This makes it easy to keep protected
credentials in a local .env file without committing them to source control (the repository's .gitignore already
ignores .env and .env.*). A sample template is provided at config/.env.example.
Discovery and precedence
Tuliprox searches for a .env file in the following order:
- Explicit CLI argument or environment variable:
-e, --env-file <PATH>orTULIPROX_ENV_FILE=<PATH>. If explicitly specified, the file must exist and have valid syntax; otherwise Tuliprox exits with an error. - Config file directory: if
-c <PATH>was provided, the parent directory of that file (e.g.<config_file_dir>/.env). - Config directory:
<config_path>/.env(default:<home_path>/config/.env). - Home directory:
<home_path>/.env. - Current working directory:
./.env.
The first matching file found is loaded.
Docker usage
There are three common ways to use .env files with Docker:
-
Automatic via mounted
configdirectory (Recommended): If yourdocker-compose.ymlmounts./config:/app/config, simply place your.envfile at./config/.envon the host. Because Tuliprox starts with-p /app/config, it automatically discovers and loads/app/config/.envat startup. -
Custom location via
TULIPROX_ENV_FILE: If your secrets file is located elsewhere on the host, mount it into the container and point theTULIPROX_ENV_FILEenvironment variable to it:services: tuliprox: image: ghcr.io/euzu/tuliprox:latest environment: - TULIPROX_ENV_FILE=/secrets/tuliprox.env volumes: - /etc/secrets/tuliprox.env:/secrets/tuliprox.env:ro - ./config:/app/config -
Native Docker Compose
env_file:directive: Docker Compose can directly populate container environment variables from a.envfile on the host without mounting:services: tuliprox: image: ghcr.io/euzu/tuliprox:latest env_file: - .env
12-factor rules: Existing environment variables take precedence
Variables already defined in the system or container environment (e.g. via export VAR=value, Docker environment:,
or Kubernetes secrets) are never overwritten by a .env file. The .env file serves as a default/fallback
configuration.
⚠️ Restart required on change (no dynamic hot-reload)
Important: Changes to
.envfiles do not take effect dynamically via configuration reload. For mounted.envfiles or bare-metal installations, restart thetuliproxservice or container (docker compose restart). When using Docker Composeenv_file:, recreate the container (docker compose up -d --force-recreate) to apply changed values.Why: In Rust, modifying environment variables in a multi-threaded runtime (
setenv) is inherently not thread-safe and can cause undefined behavior or data races with concurrent readers..envvalues are loaded strictly once at application startup and require a restart to change.
Other ways of supplying variables
Because Tuliprox resolves from the process environment, any standard mechanism works alongside or instead of .env:
export TULIPROX_WEB_SECRET=...in the shell / init script that starts the process,environment:entries in a container or compose file,Environment=lines of a service unit,- the secret store / environment settings of whatever runtime you picked.
Web UI credentials (user.txt)
Tuliprox stores password hashes, never plain text. Each line is username:argon2_hash[:group1,group2]; without
groups the user defaults to admin.
-
Generate a hash with the interactive CLI prompt (it needs a real terminal and cannot read from stdin):
tuliprox --genpwd -
Write the line into
config/user.txt(or the user file configured inconfig.yml):myuser:$argon2id$v=19$m=19456,t=2,p=1$... -
Restart the server (or reload) and verify the login.
user.txt is environment-injectable only in the sense that the path may come from a CLI/${env:...} path; the
hashes themselves are generated on the machine and are never committed. The config/user.txt shipped in this
repository contains sample hashes for the demo accounts test / nobody documented in config/README.md — replace
them before going live.
JWT secret (web_ui.auth.secret)
If web_ui.auth.secret is omitted, Tuliprox generates one in-memory and all active logins are invalidated on every
restart. For production, pin a static 64-character hexadecimal string and keep it stable:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Put the result in an environment variable and reference it from config.yml:
web_ui:
auth:
secret: "${env:TULIPROX_WEB_SECRET}"
Rotating it invalidates all sessions — do it deliberately, not on a whim.
Pre-publish checklist
Run these before pushing anything to a public repository:
# Anything obviously credential-like in tracked configs
git grep -n -i -E "(password|secret|token|api_key)[[:space:]]*:" -- config/
# Explicit values that should have been placeholders or generated on-site
git grep -n -i -E "your_|TODO|changeme|example|\.secret|localsecret" -- config/
# Staged files (never a secret should be here)
git diff --cached --stat
git status --porcelain
Other good habits:
- Keep
logging.sanitize_sensitive_info: true(default). It masks passwords, provider URLs and client IPs in logs so shared logs can't leak credentials. - Keep
runtime_config_report_enabled: falseunless you need a startup dump; when enabled it masks sensitive values as***anyway. - Never commit
data/,target/,downloads/,cache/,backup/,.env, or any runtime directory — the repository.gitignorealready covers the common ones. - If a secret ever lands in history, rotate it (it is compromised history, not just a file) and rewrite history
with
git filter-reporather than committing new secrets on top.
Related
config.ymlcore configuration —web_auth, messaging,metadata_update.tmdbsource.ymlinputs & providers — provider credentials, EPG, backup URLsapi-proxy.ymlpublished users — output user credentials and tokens- Getting Started — where config files live and how the home directory is resolved