Docker Deployment
This guide covers building and running HeliosProxy in Docker, including Docker Compose files with a PostgreSQL backend.
Docker Image
Building the Image
Create a multi-stage Dockerfile for a minimal production image:
# Stage 1: BuildFROM rust:1.86-bookworm AS builder
WORKDIR /appCOPY Cargo.toml Cargo.lock ./COPY src/ src/
# Build with desired featuresARG FEATURES="all-features,postgres-topology"RUN cargo build --release --features "${FEATURES}"
# Stage 2: RuntimeFROM debian:bookworm-slim
RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates curl && \ rm -rf /var/lib/apt/lists/*
RUN useradd --system --no-create-home heliosproxy
COPY --from=builder /app/target/release/heliosdb-proxy /usr/local/bin/heliosdb-proxy
RUN mkdir -p /etc/helios && chown heliosproxy:heliosproxy /etc/helios
USER heliosproxy
EXPOSE 6432 9090
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \ CMD curl -f http://localhost:9090/health || exit 1
ENTRYPOINT ["heliosdb-proxy"]CMD ["--config", "/etc/helios/proxy.toml"]This sample reads its configuration from /etc/helios/proxy.toml, the same path as the
published image, so every run and Compose example below works with either image.
Build the image:
docker build -t hdb-heliosdb-proxy:local .
# Build with specific featuresdocker build --build-arg FEATURES="pool-modes,postgres-topology" \ -t hdb-heliosdb-proxy:ha .Image Variants
Local build tags (the published image corresponds to the all-features build):
| Tag | Features | Size |
|---|---|---|
local | All features + PostgreSQL topology | ~35 MB |
ha | Pool modes + PostgreSQL topology (postgres / patroni providers) | ~25 MB |
minimal | Pool modes only | ~20 MB |
Transaction Replay — in-session recovery (tr_mode), the recovery journal
([journal]) and the operator-driven POST /api/replay / POST /api/shadow
endpoints — is part of the default build and is present in every variant,
minimal included; tr_enabled is the runtime switch. The ha-tr cargo feature is a
deprecated no-op kept so existing feature lists still resolve. See
Feature Flags.
The published image is ghcr.io/heliosdatabase/hdb-heliosdb-proxy. Its tags follow the
releases (each release version, its major.minor line, and latest); the examples on this
page use latest, and production deployments should pin a release tag. It starts
heliosdb-proxy --config /etc/helios/proxy.toml and contains no configuration file, so
mount yours at that path (or pass your own arguments after the image name). If you set [journal] dir, mount a
persistent volume there so committed history survives container restarts.
Running with Docker
Quick Start
# Run with a configuration file (the image reads /etc/helios/proxy.toml)docker run -d \ --name heliosproxy \ -p 6432:6432 \ -p 9090:9090 \ -v $(pwd)/proxy.toml:/etc/helios/proxy.toml:ro \ ghcr.io/heliosdatabase/hdb-heliosdb-proxy:latest
# Run with command-line arguments (these replace the image's --config argument)docker run -d \ --name heliosproxy \ -p 6432:6432 \ ghcr.io/heliosdatabase/hdb-heliosdb-proxy:latest \ --listen 0.0.0.0:6432 \ --admin 127.0.0.1:9090 \ --primary db-primary:5432 \ --standby db-standby-1:5432The admin API runs privileged operations, so the proxy refuses to start with a
non-loopback --admin address unless an admin token is set. This command-line example
keeps the admin API on the container’s loopback (reach it with docker exec). To publish
port 9090, use a configuration file that sets admin_address = "0.0.0.0:9090" and
admin_token, which also keeps the secret out of the process arguments.
Environment Variables
The proxy reads its settings from the configuration file and the command-line flags
only; it has no HELIOS_PROXY_* override variables. Inside the configuration file,
${NAME} is replaced with the value of the environment variable NAME (startup fails,
naming the variable, if it is unset) and ${NAME:-default} falls back to default:
listen_address = "0.0.0.0:6432"admin_address = "0.0.0.0:9090"admin_token = "${HELIOS_ADMIN_TOKEN}"
[pool_mode]mode = "${POOL_MODE:-transaction}"max_pool_size = ${POOL_MAX:-200}docker run -d \ --name heliosproxy \ -p 6432:6432 \ -p 9090:9090 \ -e HELIOS_ADMIN_TOKEN=replace-with-a-secret \ -e POOL_MAX=200 \ -e RUST_LOG=heliosdb_proxy=info \ -v $(pwd)/proxy.toml:/etc/helios/proxy.toml:ro \ ghcr.io/heliosdatabase/hdb-heliosdb-proxy:latestRUST_LOG, when set, takes precedence over --log-level.
Health Checks
/health, /healthz, /livez and /readyz answer without the admin token; every
other admin route needs Authorization: Bearer <admin_token> when a token is set.
# Livenessdocker exec heliosproxy curl -s http://localhost:9090/health
# Readiness (200 when at least one backend is healthy, 503 otherwise)docker exec heliosproxy curl -s http://localhost:9090/readyz
# Node status (token required when admin_token is set)docker exec heliosproxy curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://localhost:9090/nodesDocker Compose: Single Proxy with PostgreSQL
A basic setup with one proxy in front of a PostgreSQL primary.
version: "3.8"
services: heliosproxy: image: ghcr.io/heliosdatabase/hdb-heliosdb-proxy:latest ports: - "6432:6432" - "9090:9090" volumes: - ./config/proxy.toml:/etc/helios/proxy.toml:ro depends_on: pg-primary: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9090/health"] interval: 5s timeout: 3s retries: 3 restart: unless-stopped networks: - db-network
pg-primary: image: postgres:16 environment: POSTGRES_USER: helios POSTGRES_PASSWORD: helios POSTGRES_DB: appdb ports: - "5432:5432" volumes: - pg-primary-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U helios"] interval: 5s timeout: 3s retries: 5 networks: - db-network
volumes: pg-primary-data:
networks: db-network: driver: bridgeProxy configuration (config/proxy.toml):
listen_address = "0.0.0.0:6432"admin_address = "0.0.0.0:9090"# Required: a non-loopback admin bind without a token is refused at startup.admin_token = "replace-with-a-secret"tr_enabled = falsewrite_timeout_secs = 30
[pool_mode]mode = "transaction"max_pool_size = 100min_idle = 5
[pool]min_connections = 2max_connections = 50idle_timeout_secs = 300max_lifetime_secs = 1800acquire_timeout_secs = 30test_on_acquire = true
[load_balancer]read_write_split = falseread_strategy = "round_robin"latency_threshold_ms = 100
[health]check_interval_secs = 5check_timeout_secs = 3failure_threshold = 3success_threshold = 2check_query = "SELECT 1"
[[nodes]]host = "pg-primary"port = 5432role = "primary"weight = 100enabled = truename = "primary"Docker Compose: PostgreSQL with Proxy
A minimal variant for local testing, without a data volume or restart policy.
config/pg-proxy.toml takes the same keys as config/proxy.toml above:
version: "3.8"
services: heliosproxy: image: ghcr.io/heliosdatabase/hdb-heliosdb-proxy:latest ports: - "6432:6432" - "9090:9090" volumes: - ./config/pg-proxy.toml:/etc/helios/proxy.toml:ro depends_on: - pg-primary healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9090/health"] interval: 5s timeout: 3s retries: 3 networks: - pg-network
pg-primary: image: postgres:16 environment: POSTGRES_USER: appuser POSTGRES_PASSWORD: apppass POSTGRES_DB: appdb ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U appuser"] interval: 5s timeout: 3s retries: 5 networks: - pg-network
networks: pg-network:Resource Limits
Set resource constraints for production deployments:
services: heliosproxy: image: ghcr.io/heliosdatabase/hdb-heliosdb-proxy:latest deploy: resources: limits: cpus: "2.0" memory: 512M reservations: cpus: "0.5" memory: 128M ulimits: nofile: soft: 65536 hard: 65536Logging
View Proxy Logs
# Follow logsdocker compose logs -f heliosproxy
# View last 200 linesdocker compose logs --tail 200 heliosproxy
# Filter by log level (requires RUST_LOG=debug)docker compose logs heliosproxy 2>&1 | grep -i errorEnable Debug Logging
services: heliosproxy: environment: RUST_LOG: heliosdb_proxy=debugEnable JSON Structured Logging
services: heliosproxy: command: ["--config", "/etc/helios/proxy.toml", "--json-logs"]