Skip to content

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: Build
FROM rust:1.86-bookworm AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
# Build with desired features
ARG FEATURES="all-features,postgres-topology"
RUN cargo build --release --features "${FEATURES}"
# Stage 2: Runtime
FROM 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:

Terminal window
docker build -t hdb-heliosdb-proxy:local .
# Build with specific features
docker 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):

TagFeaturesSize
localAll features + PostgreSQL topology~35 MB
haPool modes + PostgreSQL topology (postgres / patroni providers)~25 MB
minimalPool 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

Terminal window
# 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:5432

The 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}
Terminal window
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:latest

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

Terminal window
# Liveness
docker 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/nodes

Docker 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: bridge

Proxy 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 = false
write_timeout_secs = 30
[pool_mode]
mode = "transaction"
max_pool_size = 100
min_idle = 5
[pool]
min_connections = 2
max_connections = 50
idle_timeout_secs = 300
max_lifetime_secs = 1800
acquire_timeout_secs = 30
test_on_acquire = true
[load_balancer]
read_write_split = false
read_strategy = "round_robin"
latency_threshold_ms = 100
[health]
check_interval_secs = 5
check_timeout_secs = 3
failure_threshold = 3
success_threshold = 2
check_query = "SELECT 1"
[[nodes]]
host = "pg-primary"
port = 5432
role = "primary"
weight = 100
enabled = true
name = "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: 65536

Logging

View Proxy Logs

Terminal window
# Follow logs
docker compose logs -f heliosproxy
# View last 200 lines
docker compose logs --tail 200 heliosproxy
# Filter by log level (requires RUST_LOG=debug)
docker compose logs heliosproxy 2>&1 | grep -i error

Enable Debug Logging

services:
heliosproxy:
environment:
RUST_LOG: heliosdb_proxy=debug

Enable JSON Structured Logging

services:
heliosproxy:
command: ["--config", "/etc/helios/proxy.toml", "--json-logs"]

See Also