Files
quicproquo/docs/src/getting-started/docker.md
Christian Nennemann 2e081ead8e chore: rename quicproquo → quicprochat in docs, Docker, CI, and packaging
Rename all project references from quicproquo/qpq to quicprochat/qpc
across documentation, Docker configuration, CI workflows, packaging
scripts, operational configs, and build tooling.

- Docker: crate paths, binary names, user/group, data dirs, env vars
- CI: workflow crate references, binary names, artifact names
- Docs: all markdown files under docs/, SDK READMEs, book.toml
- Packaging: OpenWrt Makefile, init script, UCI config (file renames)
- Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team
- Operations: Prometheus config, alert rules, Grafana dashboard
- Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths
- Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
2026-03-21 19:14:06 +01:00

6.4 KiB

Docker Deployment

quicprochat includes a multi-stage Dockerfile and a Docker Compose configuration for building and running the server in containers.


Quick start

docker compose up

This builds the server image (if not already built) and starts a single server service listening on port 7000. The server will generate a self-signed TLS certificate on first launch and begin accepting QUIC connections.

To rebuild after code changes:

docker compose up --build

To run in the background:

docker compose up -d

Docker Compose configuration

The docker-compose.yml at the repository root defines a single service:

services:
  server:
    build:
      context: .
      dockerfile: docker/Dockerfile
    ports:
      - "7000:7000/udp"
    environment:
      RUST_LOG: "info"
      QPC_LISTEN: "0.0.0.0:7000"
      QPC_DATA_DIR: "/var/lib/quicprochat"
      QPC_PRODUCTION: "true"
    volumes:
      - server-data:/var/lib/quicprochat
    restart: unless-stopped

volumes:
  server-data:

Port mapping

The container exposes port 7000 (QUIC/UDP). Note that QUIC uses UDP, so ensure your firewall allows UDP traffic on this port.

Restart policy

restart: unless-stopped ensures the server restarts automatically after crashes but stays stopped if you explicitly docker compose stop or docker compose down.


Multi-stage Docker build

The Dockerfile at docker/Dockerfile uses a two-stage build to produce a minimal runtime image.

Stage 1: Builder (rust:bookworm)

FROM rust:bookworm AS builder

WORKDIR /build

# Copy manifests first so dependency layers are cached independently of source.
COPY Cargo.toml Cargo.lock ./
COPY crates/quicprochat-core/Cargo.toml       crates/quicprochat-core/Cargo.toml
# ... (all 9 crate manifests)

# Create dummy source files for dependency caching.
RUN mkdir -p ... && echo 'fn main() {}' > ...

# Schemas must exist before the proto crate's build.rs runs.
COPY schemas/ schemas/

# Build dependencies only (cache layer).
RUN cargo build --release --bin qpc-server 2>/dev/null || true

# Copy real source and build for real.
COPY crates/ crates/
RUN cargo build --release --bin qpc-server

Key steps:

  1. Base image: rust:bookworm (Debian Bookworm with the Rust toolchain pre-installed).
  2. No system compiler required: Unlike v1, the builder stage does not install capnproto. The v2 Protobuf compiler is vendored by protobuf-src and compiled automatically as part of cargo build.
  3. Copy manifests first: Cargo.toml and Cargo.lock are copied before source code with dummy stubs so that dependency compilation is cached in a separate Docker layer.
  4. Copy schemas: The schemas/ directory is copied before the dependency build because quicprochat-proto/build.rs references it.
  5. Copy real source and build: After the dependency cache layer, real source files are copied in and cargo build --release produces the final binary.

Stage 2: Runtime (debian:bookworm-slim)

FROM debian:bookworm-slim AS runtime

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /build/target/release/qpc-server /usr/local/bin/qpc-server

RUN groupadd --system qpc \
    && useradd --system --gid qpc --no-create-home --shell /usr/sbin/nologin qpc \
    && mkdir -p /var/lib/quicprochat \
    && chown qpc:qpc /var/lib/quicprochat

EXPOSE 7000

VOLUME ["/var/lib/quicprochat"]

ENV RUST_LOG=info \
    QPC_LISTEN=0.0.0.0:7000 \
    QPC_DATA_DIR=/var/lib/quicprochat \
    QPC_TLS_CERT=/var/lib/quicprochat/server-cert.der \
    QPC_TLS_KEY=/var/lib/quicprochat/server-key.der \
    QPC_PRODUCTION=true

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
    CMD test -f /var/lib/quicprochat/server-cert.der || exit 1

USER qpc

CMD ["qpc-server"]

Key characteristics:

  • Minimal image: No Rust toolchain, no build tools, no protoc binary.
  • ca-certificates: Included for future HTTPS calls (e.g., ACME certificate provisioning).
  • Dedicated user: The container runs as the qpc system user (not root) for defense in depth.
  • Named volume: /var/lib/quicprochat is declared as a VOLUME for data persistence.
  • QPC_PRODUCTION=true: The runtime image defaults to production mode, requiring pre-existing TLS certificates and a strong auth token.

Volume persistence

The server stores its state (TLS certificates, KeyPackages, delivery queues, OPAQUE setup, KT log) in the data directory. Mount a volume to persist this data across container restarts:

services:
  server:
    # ... existing config ...
    volumes:
      - server-data:/var/lib/quicprochat

volumes:
  server-data:

Or use a bind mount for easier inspection:

mkdir -p ./server-data

docker run -d \
  --name quicprochat \
  -p 7000:7000/udp \
  -v "$(pwd)/server-data:/var/lib/quicprochat" \
  -e QPC_ALLOW_INSECURE_AUTH=true \
  -e QPC_PRODUCTION=false \
  -e RUST_LOG=info \
  qpc-server

Without a volume, all server state (including TLS certificates and message queues) is lost when the container is removed. The server will generate a new self-signed certificate on each fresh start, which means clients will need the new certificate to connect.


Building just the image

To build the Docker image without starting a container:

docker build -t qpc-server -f docker/Dockerfile .

To run it in development mode (without production validation):

docker run -d \
  --name quicprochat \
  -p 7000:7000/udp \
  -e QPC_ALLOW_INSECURE_AUTH=true \
  -e QPC_PRODUCTION=false \
  -e RUST_LOG=info \
  qpc-server

Connecting the client to a containerised server

When the server runs in Docker with docker compose up, the client can connect from the host:

# Extract the server's TLS cert from the container volume
docker compose cp server:/var/lib/quicprochat/server-cert.der ./data/server-cert.der

# Connect
cargo run -p quicprochat-client -- ping \
  --ca-cert ./data/server-cert.der \
  --server-name localhost

If you mounted a bind volume (e.g., ./server-data:/var/lib/quicprochat), the certificate is directly accessible at ./server-data/server-cert.der.


Next steps