Server Block
The server block in snakeway.hcl controls process-level settings.
Configuration Example
server {
version = 1 # Config format version (always 1 for now)
pid_file = "/var/run/snakeway.pid" # Optional; enables signal-based reload
threads = 8 # Worker threads for the proxy runtime
ca_file = "/path/to/certs/ca.pem" # Global CA for verifying upstream TLS
dns_refresh_interval_seconds = 30 # How often to re-resolve upstream hostnames
shutdown {
drain_seconds = 10 # Time for active connections to finish
force_timeout_seconds = 30 # Hard ceiling on total shutdown time
}
upgrade {
sock = "/var/run/snakeway_upgrade.sock"
max_retries = 5
}
performance {
work_stealing = true # Allow work stealing between threads
upstream_connection_pool_size = 128 # Idle upstream connections per worker
parallel_accepts_per_listener = 1 # Parallel accept tasks per listener
}
upstream_source_addresses {
ipv4 = ["10.0.1.5"]
ipv6 = ["fd00::1"]
}
}
Field Reference
version integer, required. Configuration file format version. Currently always 1.
pid_file string, default: none. Path where Snakeway writes its PID on startup. Enables the snakeway reload command.
threads integer, default: defers to the Pingora runtime. Number of worker threads for request processing. For most
deployments, set this to the number of CPU cores on your server.
ca_file string, default: none. Path to a CA certificate file used to verify upstream TLS connections when no
per-upstream ca_file is configured.
dns_refresh_interval_seconds integer, default: 30. How often (in seconds) Snakeway re-resolves
upstream hostnames in the background. When an upstream is configured with a hostname rather than an
IP address, Snakeway resolves it at startup and then periodically refreshes the resolved address on
this interval. This allows DNS changes (e.g., rolling deployments, blue-green switches) to take
effect without a config reload. Valid range: 1 to 3600.
tls_automation object, default: none. Configures automatic ACME certificate issuance and renewal.
See TLS Automation.
If threads is not set, Snakeway defers entirely to the Pingora runtime's internal defaults rather than selecting a
value on your behalf.
Shutdown
The shutdown block controls how Snakeway terminates when it receives a stop signal (SIGTERM).
shutdown {
drain_seconds = 10
force_timeout_seconds = 30
}
When Snakeway receives SIGTERM, it stops accepting new connections and gives active requests a window to complete naturally. After the drain window expires, any remaining connections are forcefully dropped.
shutdown.drain_seconds integer, default: 10. How long (in seconds) active connections are
allowed to finish after a shutdown signal. Requests that complete within this window are guaranteed
not to be dropped. Set to 0 to skip the drain phase entirely. Valid range: 0 to 300.
shutdown.force_timeout_seconds integer, default: none. Hard ceiling on the final cleanup step
of graceful shutdown. After this timeout, remaining connections are forcefully terminated regardless
of state. When not set, the cleanup step has no upper bound beyond the drain period. Valid range:
1 to 300.
If neither field is configured and a long-lived connection (WebSocket, gRPC stream) never closes,
systemd's TimeoutStopSec becomes the only backstop. Always configure drain_seconds or set
TimeoutStopSec in your service unit.
Upgrade
The upgrade block configures zero-drop upgrades, where a new Snakeway process takes over
listener file descriptors from the old process without dropping connections.
upgrade {
sock = "/var/run/snakeway_upgrade.sock"
max_retries = 10
}
upgrade.sock string, default: /tmp/pingora_upgrade.sock. Path to the Unix domain socket used
for transferring listener file descriptors between old and new processes during a zero-drop upgrade.
Both processes must agree on this path. Set a unique value when running multiple Snakeway instances
on the same host.
upgrade.max_retries integer, default: 5. Maximum number of retries when connecting to or
accepting on the upgrade socket during FD transfer. Each retry waits one second. Valid range: 1
to 60.
See the Hot Reload internals page for details on how the upgrade mechanism works.
Performance
The performance block contains runtime tuning knobs that affect throughput and resource usage.
performance {
work_stealing = true
upstream_connection_pool_size = 256
parallel_accepts_per_listener = 4
}
performance.work_stealing boolean, default: true. Controls whether worker threads are allowed
to steal tasks from one another. Enabling work stealing improves throughput under uneven load.
performance.upstream_connection_pool_size integer, default: 128 (Pingora default). Number of
idle keepalive connections maintained per worker thread to upstream servers. Increase this for
high-traffic deployments with many backends; decrease it on memory-constrained hosts. Valid range:
1 to 65535.
performance.parallel_accepts_per_listener integer, default: 1 (Pingora default). Number of
parallel accept tasks spawned per listener file descriptor. Higher values reduce contention under
bursty connection rates (thundering herd). Most deployments do not need to change this. Valid range:
1 to 64.
Upstream Source Addresses
The upstream_source_addresses block controls which local IP addresses Snakeway uses as the
source when making outbound connections to upstreams.
upstream_source_addresses {
ipv4 = ["10.0.1.5", "10.0.1.6"]
ipv6 = ["fd00::1"]
}
By default, the operating system selects the source address based on its routing table. Use this block when you need to pin outbound traffic to specific interfaces or source IPs.
upstream_source_addresses.ipv4 list of strings, default: empty. IPv4 addresses to bind outbound
upstream connections to. When multiple addresses are specified, Snakeway round-robins across them.
upstream_source_addresses.ipv6 list of strings, default: empty. IPv6 addresses to bind outbound
upstream connections to. When multiple addresses are specified, Snakeway round-robins across them.
Common use cases:
- Multi-homed hosts — force upstream traffic through a specific network interface
- Upstream IP allowlists — guarantee which source IP upstreams see
- Port exhaustion — spread connections across multiple source IPs to get more ephemeral ports
Observability
The observability block configures telemetry export.
It is optional; when omitted, no telemetry data is exported.
Configuration Example
server {
# ... other fields ...
observability {
otel {
enable = true
endpoint = "http://localhost:4317"
service_name = "my-proxy"
sampling_ratio = 0.1 # sample 10% of root traces
}
}
}
Field Reference
observability block, optional. Contains telemetry configuration.
observability.otel block, optional. Configures OpenTelemetry export.
observability.otel.enable boolean, required. Enables or disables
the OpenTelemetry exporter.
observability.otel.endpoint string, required when enabled. gRPC
endpoint for the OTLP exporter (e.g., http://localhost:4317).
observability.otel.service_name string, required when enabled.
Value used for the service.name resource attribute in exported
telemetry.
observability.otel.sampling_ratio float, default: 1.0. Controls
what fraction of root traces are sampled (0.0 to 1.0). When an incoming
request carries a sampled W3C Trace Context, Snakeway always honors
that decision. For requests without a parent context, this ratio
determines sampling probability. Set to 0.1 to sample 10% of root
traces, or 1.0 to sample all.
PID File and Reload Behavior
When pid_file is set, Snakeway writes its process ID to the specified path at startup. This enables the
snakeway reload command, which sends a signal to the running process to reload configuration without a full restart.
The PID file is useful when integrating Snakeway with:
- Process supervisors
- Signal-based reload workflows
- System scripts or orchestration tools
If the PID file cannot be written, Snakeway logs a warning and continues running. On shutdown, Snakeway removes the PID file as a best-effort cleanup step.