Guide

Configuration

Environment variables, config files, feature flags


Overview

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.


Prerequisites

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 twisted to 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

Installation

Follow these steps to install tlsproxy into your Python environment.

  1. Create and activate a virtual environment (recommended to avoid dependency conflicts):
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
  1. Install tlsproxy via pip:
pip install tlsproxy
  1. 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.

  1. 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.

  1. 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"

Configuration

tlsproxy reads its configuration from three sources, applied in this priority order (highest to lowest):

  1. Environment variables — override everything else, useful in containerized deployments
  2. Config file (tlsproxy.yaml) — the primary place for persistent settings
  3. 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

KeyEnv VariableDefaultValid ValuesEffect
listen.hostTLSPROXY_LISTEN_HOST0.0.0.0Any valid IP or hostnameInterface on which the proxy accepts TLS connections
listen.portTLSPROXY_LISTEN_PORT84431–65535Port the proxy listens on for incoming TLS traffic
backend.hostTLSPROXY_BACKEND_HOST127.0.0.1Any valid IP or hostnameHost of the plain-text backend service
backend.portTLSPROXY_BACKEND_PORT80801–65535Port of the plain-text backend service
tls.cert_fileTLSPROXY_TLS_CERT(none, required)Absolute or relative pathPath to PEM-encoded TLS certificate
tls.key_fileTLSPROXY_TLS_KEY(none, required)Absolute or relative pathPath to PEM-encoded private key
tls.ca_fileTLSPROXY_TLS_CA(none)Absolute or relative pathPath to CA bundle; enables mutual TLS (mTLS) when set
tls.min_versionTLSPROXY_TLS_MIN_VERSIONTLSv1.2TLSv1.2, TLSv1.3Minimum TLS protocol version accepted; set to TLSv1.3 in high-security environments
log.levelTLSPROXY_LOG_LEVELINFODEBUG, INFO, WARNING, ERRORControls verbosity of structured log output
log.fileTLSPROXY_LOG_FILE(stdout)Absolute or relative pathRedirect logs to a file instead of standard output
timeout.connectTLSPROXY_TIMEOUT_CONNECT10Integer seconds > 0Seconds to wait for a backend connection before returning a 502-equivalent error
timeout.idleTLSPROXY_TIMEOUT_IDLE60Integer seconds > 0Seconds 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.

FlagEnv VariableDefaultEffect
features.access_logTLSPROXY_FEATURE_ACCESS_LOGtrueLogs each proxied request with method, path, and response code
features.health_endpointTLSPROXY_FEATURE_HEALTH_ENDPOINTtrueExposes GET /healthz on the listen port for liveness checks
features.metrics_endpointTLSPROXY_FEATURE_METRICS_ENDPOINTfalseExposes GET /metricz with Prometheus-compatible connection counters
features.proxy_protocolTLSPROXY_FEATURE_PROXY_PROTOCOLfalsePrepends 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

Usage

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.


Examples

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)

Troubleshooting

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_file or tls.key_file in 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:
    ls -l ./certs/server.crt ./certs/server.key
    
    Use absolute paths in production to avoid working-directory ambiguity.

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 /healthz returns 404 even though the proxy is running and forwarding other traffic.
  • Likely cause: features.health_endpoint is set to false in your config, or the environment variable TLSPROXY_FEATURE_HEALTH_ENDPOINT=false is overriding the config file.
  • Fix: Set features.health_endpoint: true in your config file, or unset / set the environment variable to true. 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 timeout errors.
  • Likely cause: timeout.connect is too low for your network or the backend is slow to accept new connections under load.
  • Fix: Increase timeout.connect in your config:
    timeout:
      connect: 30
    
    Also check that the backend is not exhausting its connection pool. Enable log.level: DEBUG to 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.1 instead 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:
    features:
      proxy_protocol: true
    
    Your backend must also be configured to parse the PROXY protocol header; otherwise it will misread the first bytes of every request.

Problem: Config file changes have no effect after SIGHUP

  • Symptom: You edit tlsproxy.yaml and send SIGHUP, but the proxy continues using old values.
  • Likely cause: You changed listen.host or listen.port, which cannot be reloaded live because they require re-binding the socket.
  • Fix: For listen address/port changes, perform a full restart:
    tlsproxy stop --config ./config/tlsproxy.yaml
    tlsproxy start --config ./config/tlsproxy.yaml
    
    All other settings (backend address, TLS files, timeouts, log level, feature flags) are reloadable via SIGHUP.