You are reading the docs for rws 18.3 The latest release is 18.8. Read this page for the latest release.

Docker

Build and run rust-web-server in a minimal Docker container using multi-stage builds.

Prebuilt image

Every release publishes a multi-arch image (linux/amd64, linux/arm64) to Docker Hub as bohdaq/rws and to the GitHub Container Registry as ghcr.io/bohdaq/rws, tagged with the full version (17.113.0), the major version (17), and latest:

# Serve the current directory
docker run -p 7878:7878 -v "$PWD:/srv:ro" bohdaq/rws

# Pin a version (recommended in production)
docker run -p 7878:7878 -v "$PWD:/srv:ro" bohdaq/rws:17.113.0

The image is gcr.io/distroless/static-debian12:nonroot plus the static musl rws binary (about 12 MB) — no shell, no package manager, runs as a non-root user. The working directory is /srv: mount static content there, or put an rws.config.toml there to run as a config-driven proxy. Configure with RWS_CONFIG_* environment variables, e.g. -e RWS_CONFIG_PORT=8080. Each release image is started and checked (/healthz, the served page, --version) before it's published.

Automatic HTTPS in Docker

rws writes the Let's Encrypt certificate, its key and the ACME account key to /data (owned by the image's user). Mount a volume there so restarts reuse the certificate instead of requesting a new one:

docker run -d --name rws -p 80:80 -p 443:443 -p 443:443/udp \
  -v "$PWD:/srv:ro" -v rws-data:/data bohdaq/rws

with an [acme] section in the mounted rws.config.toml (see Automatic HTTPS). 443/udp carries HTTP/3.

Build your own image with the Dockerfile below if you need non-default Cargo features or your own application code.

Multi-stage Dockerfile

The recommended Dockerfile uses a two-stage build: a rust:1.88 builder compiles the binary, then a slim Debian image carries only the final executable.

# ── Stage 1: build ────────────────────────────────────────────────────────────
FROM rust:1.88 AS builder
WORKDIR /app

# Cache dependencies before copying source.
COPY Cargo.toml Cargo.lock ./
# Build a dummy main to prime the dependency cache.
RUN mkdir src && echo 'fn main(){}' > src/main.rs && cargo build --release && rm src/main.rs

# Now copy real source and build.
COPY . .
RUN cargo build --release

# ── Stage 2: runtime ──────────────────────────────────────────────────────────
FROM debian:bookworm-slim

# Runtime dependencies for TLS (ca-certificates) and diagnostics (curl).
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates curl \
 && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/rws /usr/local/bin/rws

# TCP port (HTTP/1.1, HTTP/2)
EXPOSE 7878
# UDP port (HTTP/3 / QUIC) — only relevant with the http3 feature
EXPOSE 7878/udp

HEALTHCHECK --interval=15s --timeout=3s --start-period=5s \
  CMD curl -f http://localhost:7878/healthz || exit 1

CMD ["rws"]
Dependency cache layer

The dummy src/main.rs trick builds and caches all Cargo dependencies before the real source is copied. Subsequent builds that only change application code skip the full cargo build of your dependencies.

Image sizes by feature flag

The default cargo build --release enables the http3 feature, which includes QUIC, HTTP/2, TLS (rustls), and the quinn/h3 stacks. Lighter builds are available:

Feature flag TLS HTTP/2 HTTP/3 Approx. final image
http1 (no-default) No No No ~3 MB
http2 (no-default) Yes (rustls) Yes No ~7 MB
http3 (default) Yes (rustls) Yes Yes ~12 MB

To build with the http1-only feature (smallest image):

RUN cargo build --release --no-default-features --features http1

Exposed ports

Port Protocol Purpose
7878 TCP HTTP/1.1 and HTTP/2 (TLS upgrade via ALPN)
7878/udp UDP HTTP/3 / QUIC (only active when a TLS cert is configured)

Change the port at runtime with RWS_CONFIG_PORT.

Environment variable injection

All RWS_CONFIG_* variables can be passed via docker run -e or a .env file:

docker run \
  -e RWS_CONFIG_IP=0.0.0.0 \
  -e RWS_CONFIG_PORT=8080 \
  -e RWS_CONFIG_THREAD_COUNT=8 \
  -e RWS_CONFIG_LOG_FORMAT=json \
  -p 8080:8080 \
  my-rws-image

docker-compose with TLS

Mount your certificate files as a read-only volume and pass the paths via environment variables:

# docker-compose.yml
version: "3.9"

services:
  rws:
    build: .
    ports:
      - "443:7878"
      - "443:7878/udp"   # QUIC / HTTP/3
      - "80:7879"         # HTTP → HTTPS redirect port
    environment:
      RWS_CONFIG_IP: "0.0.0.0"
      RWS_CONFIG_PORT: "7878"
      RWS_CONFIG_TLS_CERT_FILE: "/run/secrets/cert.pem"
      RWS_CONFIG_TLS_KEY_FILE: "/run/secrets/key.pem"
      RWS_CONFIG_HTTP_REDIRECT_PORT: "7879"
      RWS_CONFIG_LOG_FORMAT: "json"
    volumes:
      - ./certs/cert.pem:/run/secrets/cert.pem:ro
      - ./certs/key.pem:/run/secrets/key.pem:ro
    healthcheck:
      test: ["CMD", "curl", "-fk", "https://localhost:7878/healthz"]
      interval: 15s
      timeout: 3s
      retries: 3
      start_period: 10s
    restart: unless-stopped
SIGHUP hot-reload inside Docker

Send docker kill --signal=SIGHUP <container> to reload TLS certificates, CORS rules, rate limits, and log format without restarting the container. The server's /admin/config/reload endpoint does the same over HTTP.

Edit this page on GitHub