Configuration
Environment variables, config files, feature flags
This page explains how to configure tlsproxy, the Twisted-based TLS termination proxy that accepts encrypted client connections and forwards them to plain-text backend services. Configuration is driven by a combination of environment variables, a YAML or INI-style config file, and optional feature flags that let you tune TLS behavior, routing, and logging without modifying source code. Getting the configuration right is essential before you start the proxy — an incorrect certificate path or a mismatched backend address will cause the proxy to refuse connections silently or fail to start entirely.
Before you configure tlsproxy, make sure you have the following in place:
- Python 3.8 or later installed and available on your
PATH - Twisted 22.0 or later (
pip show twistedto verify) - tlsproxy installed (see the Installation section below)
- A valid TLS certificate and private key for the listening interface (PEM format)
- Network access to your backend service (the plain-text service tlsproxy will forward traffic to)
- Write permission to the directory where you intend to store the config file and log output
- Basic familiarity with environment variables and editing text-based config files
Follow these steps to install tlsproxy into your Python environment.
- Create and activate a virtual environment (recommended to avoid dependency conflicts):
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
- Install tlsproxy via pip:
pip install tlsproxy
- Verify the installation by checking the version:
tlsproxy --version
You should see a version string printed to stdout, confirming the binary is on your PATH.
- Install the default configuration template into your working directory:
tlsproxy init --config-dir ./config
This creates ./config/tlsproxy.yaml with commented-out defaults you can edit. It does not start the proxy.
- Confirm Twisted is available at the correct version:
python3 -c "import twisted; print(twisted.__version__)"
If the import fails, install Twisted explicitly:
pip install "twisted>=22.0"
tlsproxy reads its configuration from three sources, applied in this priority order (highest to lowest):
- Environment variables — override everything else, useful in containerized deployments
- Config file (
tlsproxy.yaml) — the primary place for persistent settings - Built-in defaults — applied when a value is absent from both sources
Config file location
By default, tlsproxy looks for tlsproxy.yaml in the current working directory. Override this with the --config flag or the TLSPROXY_CONFIG environment variable.
Core settings
| Key | Env Variable | Default | Valid Values | Effect |
|---|---|---|---|---|
listen.host | TLSPROXY_LISTEN_HOST | 0.0.0.0 | Any valid IP or hostname | Interface on which the proxy accepts TLS connections |
listen.port | TLSPROXY_LISTEN_PORT | 8443 | 1–65535 | Port the proxy listens on for incoming TLS traffic |
backend.host | TLSPROXY_BACKEND_HOST | 127.0.0.1 | Any valid IP or hostname | Host of the plain-text backend service |
backend.port | TLSPROXY_BACKEND_PORT | 8080 | 1–65535 | Port of the plain-text backend service |
tls.cert_file | TLSPROXY_TLS_CERT | (none, required) | Absolute or relative path | Path to PEM-encoded TLS certificate |
tls.key_file | TLSPROXY_TLS_KEY | (none, required) | Absolute or relative path | Path to PEM-encoded private key |
tls.ca_file | TLSPROXY_TLS_CA | (none) | Absolute or relative path | Path to CA bundle; enables mutual TLS (mTLS) when set |
tls.min_version | TLSPROXY_TLS_MIN_VERSION | TLSv1.2 | TLSv1.2, TLSv1.3 | Minimum TLS protocol version accepted; set to TLSv1.3 in high-security environments |
log.level | TLSPROXY_LOG_LEVEL | INFO | DEBUG, INFO, WARNING, ERROR | Controls verbosity of structured log output |
log.file | TLSPROXY_LOG_FILE | (stdout) | Absolute or relative path | Redirect logs to a file instead of standard output |
timeout.connect | TLSPROXY_TIMEOUT_CONNECT | 10 | Integer seconds > 0 | Seconds to wait for a backend connection before returning a 502-equivalent error |
timeout.idle | TLSPROXY_TIMEOUT_IDLE | 60 | Integer seconds > 0 | Seconds of inactivity before closing an idle proxied connection |
Feature flags
Feature flags are boolean values that toggle optional proxy behaviors. Set them in the config file under the features key, or via environment variables using TLSPROXY_FEATURE_<FLAG_NAME>=true|false.
| Flag | Env Variable | Default | Effect |
|---|---|---|---|
features.access_log | TLSPROXY_FEATURE_ACCESS_LOG | true | Logs each proxied request with method, path, and response code |
features.health_endpoint | TLSPROXY_FEATURE_HEALTH_ENDPOINT | true | Exposes GET /healthz on the listen port for liveness checks |
features.metrics_endpoint | TLSPROXY_FEATURE_METRICS_ENDPOINT | false | Exposes GET /metricz with Prometheus-compatible connection counters |
features.proxy_protocol | TLSPROXY_FEATURE_PROXY_PROTOCOL | false | Prepends a PROXY protocol header to backend connections so the backend can read the original client IP |
Annotated config file example
listen:
host: 0.0.0.0 # Accept connections on all interfaces
port: 8443 # Standard alternate HTTPS port
backend:
host: 127.0.0.1 # Backend runs on the same host
port: 8080
tls:
cert_file: ./certs/server.crt
key_file: ./certs/server.key
# ca_file: ./certs/ca.crt # Uncomment to enable mTLS
min_version: TLSv1.2
log:
level: INFO
# file: ./logs/tlsproxy.log # Uncomment to log to a file
timeout:
connect: 10
idle: 60
features:
access_log: true
health_endpoint: true
metrics_endpoint: false
proxy_protocol: false
Once your config file is in place, use the following patterns to operate tlsproxy day-to-day.
Start the proxy
Point tlsproxy at your config file and start it in the foreground:
tlsproxy start --config ./config/tlsproxy.yaml
The proxy prints its resolved configuration summary at startup (at INFO level) so you can confirm the values it read.
Start with environment variable overrides
In CI or container environments, you may not have a config file. Supply the minimum required values as environment variables:
export TLSPROXY_TLS_CERT=/run/secrets/server.crt
export TLSPROXY_TLS_KEY=/run/secrets/server.key
export TLSPROXY_BACKEND_HOST=my-backend-service
export TLSPROXY_BACKEND_PORT=3000
tlsproxy start
Verify connectivity
Once the proxy is running, confirm it accepts TLS connections:
curl -v --cacert ./certs/ca.crt https://localhost:8443/healthz
A healthy proxy returns HTTP 200 OK with a JSON body (requires features.health_endpoint: true).
Reload configuration without downtime
Send SIGHUP to the running process to reload the config file without dropping existing connections:
kill -HUP $(cat /var/run/tlsproxy.pid)
Note: changes to listen.host and listen.port require a full restart — they cannot be reloaded live.
Stop the proxy gracefully
tlsproxy stop --config ./config/tlsproxy.yaml
Or send SIGTERM to let in-flight connections drain before the process exits:
kill -TERM $(cat /var/run/tlsproxy.pid)
Enable debug logging temporarily
Override the log level at runtime without editing the config file:
TLSPROXY_LOG_LEVEL=DEBUG tlsproxy start --config ./config/tlsproxy.yaml
Switch back to INFO by removing the environment variable and restarting.
The following examples are complete and runnable. Each assumes tlsproxy is installed and that you have valid TLS certificates in ./certs/.
Example 1 — Minimal local development setup
Forward HTTPS on port 8443 to a locally running HTTP dev server on port 3000.
# config/dev.yaml
listen:
port: 8443
backend:
host: 127.0.0.1
port: 3000
tls:
cert_file: ./certs/dev.crt
key_file: ./certs/dev.key
log:
level: DEBUG
tlsproxy start --config config/dev.yaml
Expected output:
[INFO] tlsproxy starting
[INFO] listening on 0.0.0.0:8443 (TLS)
[INFO] forwarding to 127.0.0.1:3000 (plain-text)
[INFO] health endpoint enabled at /healthz
Example 2 — Enable mTLS for client certificate authentication
Require clients to present a certificate signed by your internal CA.
# config/mtls.yaml
listen:
port: 8443
backend:
host: 127.0.0.1
port: 8080
tls:
cert_file: ./certs/server.crt
key_file: ./certs/server.key
ca_file: ./certs/internal-ca.crt # triggers mTLS
min_version: TLSv1.3
log:
level: INFO
tlsproxy start --config config/mtls.yaml
Test with a valid client certificate:
curl --cert ./certs/client.crt \
--key ./certs/client.key \
--cacert ./certs/internal-ca.crt \
https://localhost:8443/healthz
Expected output:
{"status": "ok"}
A request without a client certificate returns a TLS handshake error and no HTTP response body.
Example 3 — Expose Prometheus metrics
Enable the metrics endpoint for integration with a scraping pipeline.
# config/metrics.yaml
listen:
port: 8443
backend:
host: 127.0.0.1
port: 8080
tls:
cert_file: ./certs/server.crt
key_file: ./certs/server.key
features:
metrics_endpoint: true
tlsproxy start --config config/metrics.yaml
Scrape metrics:
curl -k https://localhost:8443/metricz
Expected output (Prometheus text format):
# HELP tlsproxy_connections_total Total proxied connections since start
# TYPE tlsproxy_connections_total counter
tlsproxy_connections_total 42
# HELP tlsproxy_connections_active Currently open proxied connections
# TYPE tlsproxy_connections_active gauge
tlsproxy_connections_active 3
Example 4 — Container deployment using only environment variables
No config file; all settings injected at runtime.
docker run --rm \
-e TLSPROXY_LISTEN_PORT=443 \
-e TLSPROXY_BACKEND_HOST=app-service \
-e TLSPROXY_BACKEND_PORT=80 \
-e TLSPROXY_TLS_CERT=/run/secrets/tls.crt \
-e TLSPROXY_TLS_KEY=/run/secrets/tls.key \
-e TLSPROXY_FEATURE_HEALTH_ENDPOINT=true \
-p 443:443 \
myregistry/tlsproxy:latest
Expected startup log:
[INFO] tlsproxy starting
[INFO] config source: environment variables only
[INFO] listening on 0.0.0.0:443 (TLS)
[INFO] forwarding to app-service:80 (plain-text)
Use the symptom → cause → fix format below to diagnose the most common configuration problems.
Problem: Proxy exits immediately with certificate file not found
- Symptom: tlsproxy prints an error at startup and exits with a non-zero code before accepting any connections.
- Likely cause:
tls.cert_fileortls.key_filein your config file (or the corresponding environment variables) point to a path that does not exist or is not readable by the process user. - Fix: Verify the paths are correct and that the running user has read permission:
Use absolute paths in production to avoid working-directory ambiguity.
ls -l ./certs/server.crt ./certs/server.key
Problem: curl returns a TLS handshake error (SSL_ERROR_RX_RECORD_TOO_LONG or similar)
- Symptom: Connecting to the proxy with curl or a browser fails before any HTTP response is received.
- Likely cause: The backend service is also speaking TLS, and tlsproxy is forwarding TLS-to-TLS instead of TLS-to-plain-text. tlsproxy is a termination proxy — the backend must accept plain HTTP.
- Fix: Configure your backend to listen on plain HTTP, not HTTPS. If you need end-to-end TLS, run a separate TLS listener on the backend and remove tlsproxy from that path.
Problem: Health endpoint returns 404 Not Found
- Symptom:
GET /healthzreturns 404 even though the proxy is running and forwarding other traffic. - Likely cause:
features.health_endpointis set tofalsein your config, or the environment variableTLSPROXY_FEATURE_HEALTH_ENDPOINT=falseis overriding the config file. - Fix: Set
features.health_endpoint: truein your config file, or unset / set the environment variable totrue. Restart the proxy for the change to take effect (this flag cannot be hot-reloaded).
Problem: Backend connections time out under load
- Symptom: Requests succeed under low traffic but intermittently receive no response, and the log shows
backend connect timeouterrors. - Likely cause:
timeout.connectis too low for your network or the backend is slow to accept new connections under load. - Fix: Increase
timeout.connectin your config:Also check that the backend is not exhausting its connection pool. Enabletimeout: connect: 30log.level: DEBUGto see per-connection timing.
Problem: Client IP appears as 127.0.0.1 in backend logs
- Symptom: Your backend service logs all requests as originating from
127.0.0.1instead of the real client IP. - Likely cause: By default, tlsproxy opens a new plain-text connection to the backend and the original client IP is not forwarded.
- Fix: Enable the PROXY protocol feature so the backend receives the original client address:
Your backend must also be configured to parse the PROXY protocol header; otherwise it will misread the first bytes of every request.
features: proxy_protocol: true
Problem: Config file changes have no effect after SIGHUP
- Symptom: You edit
tlsproxy.yamland sendSIGHUP, but the proxy continues using old values. - Likely cause: You changed
listen.hostorlisten.port, which cannot be reloaded live because they require re-binding the socket. - Fix: For listen address/port changes, perform a full restart:
All other settings (backend address, TLS files, timeouts, log level, feature flags) are reloadable via
tlsproxy stop --config ./config/tlsproxy.yaml tlsproxy start --config ./config/tlsproxy.yamlSIGHUP.