Skip to content

Multi Container

Tommi Grönlund edited this page Oct 27, 2026 · 5 revisions

Documentation for the image tag 1.0.0-rc1.1. See Versions for the documentation of a release.

Multi Container

One container per service (identity, API, web, ACME, EST, queue consumer) plus Redis, all from the same image on one host with one shared volume. Choose it when you need EST, or want ACME and EST reachable on a wider network without exposing the API, identity or web UI.

It is not scale-out. Run exactly one of each service.

Quick start

  1. Get the image: Requirements and Getting the Image. You need the deploy/multi-container/ folder from the same release (it holds docker-compose.yml and .env.example).

  2. Create your settings file:

    cp deploy/multi-container/.env.example deploy/multi-container/.env

    Edit .env (values below).

  3. Provide an EST server certificate and key (see EST).

  4. Start:

    docker compose -f deploy/multi-container/docker-compose.yml \
      --env-file deploy/multi-container/.env up -d

Services start in dependency order on health: Redis and identity, then the API, then web, ACME, EST and the queue consumer. Browse to http://<QERTUM_HOST>:8080 and sign in as admin, as described under Single Container.

Settings (.env)

Variable Required Meaning
QERTUM_IMAGE yes Image reference
QERTUM_HOST yes Name or IP that browsers, ACME/EST clients and the other containers use. Not localhost: inside a container that is the container itself, and apiservice and webfrontend refuse to start (exit 64). On Docker Desktop, host.docker.internal works for a local trial
QERTUM_EST_TLS_CERT, QERTUM_EST_TLS_KEY yes Host paths of the EST server certificate chain and key (PEM)
QERTUM_DATA no Host path or volume for /data (default: named volume qertum-data)
QERTUM_SCHEME no http (default) or https for web, identity and API when a proxy terminates TLS for them. Never for ACME or EST
WEB_PORT, IDENTITY_PORT, API_PORT, ACME_PORT, EST_PORT no Advertised and published ports (defaults 8080–8084). Containers always listen on 8080–8084 inside
ACME_SCHEME no http (default) or https; the scheme ACME advertises. Use https only when the ACME container terminates TLS itself
SEED_ADMIN_PASSWORD no First-run admin password. Unset means password, which the first login forces you to change

Layout

  • Containers are named qertum-<service> (qertum-apiservice, qertum-cache, ...). The backup scripts depend on these names; do not rename them.
  • Every Qertum container mounts the same /data. The layout is the same as the single container's.
  • Redis is its own container on the internal network and is never published.
  • Published ports: web, identity, API, ACME and EST. The queue consumer has none. To expose only ACME and EST beyond the LAN, bind those two ports to the outward interface and leave the others on the LAN: change the ports: entries to "<ip>:<port>:<container port>".

EST: TLS terminated by the service

EST re-enrollment (/simplereenroll) uses the client certificate as the credential, so the EST container terminates TLS itself. Mount a PEM certificate chain and key through QERTUM_EST_TLS_CERT and QERTUM_EST_TLS_KEY.

  • The key must be readable by UID 1654, for example chown 1654 tls.key && chmod 400 tls.key.
  • The service refuses to start without both files. It never falls back to plain HTTP.
  • Clients must trust the issuer of that server certificate. Qertum does not issue it for you (a bootstrap circle); use your existing PKI, or a self-signed certificate for a lab.

By default Est:RequireHttps is on, so /simpleenroll over plain HTTP answers 401.

ACME: no TLS-terminating proxy

ACME clients sign the request URL into every request, and the service compares it with the scheme and host it sees. Behind a TLS-terminating proxy it answers "URL mismatch". This is a requirement, not a setting to work around. By default ACME speaks plain HTTP on ACME_PORT.

To have ACME serve TLS itself, add a Compose override for acmeservice that sets ASPNETCORE_URLS=https://0.0.0.0:8083, Kestrel__Certificates__Default__Path and Kestrel__Certificates__Default__KeyPath, and mounts the PEM files. Then set ACME_SCHEME=https in .env so the directory and order links advertise https.

TPM-sealed keys (optional)

To seal the key ring to a TPM 2.0 chip, pass the device and its group to every container with the opt-in override:

TPM_DEVICE=/dev/tpmrm0 TPM_GROUP=105 docker compose \
  -f deploy/multi-container/docker-compose.yml \
  -f deploy/multi-container/docker-compose.tpm.yml \
  --env-file deploy/multi-container/.env up -d

A TPM-sealed key never leaves that TPM, so a backup restored onto other hardware cannot decrypt the CA key. Read Backup and Restore before enabling it. It has been run on one x86_64 TPM 2.0 (migration to the TPM, restart, issuing before and after, leaving TPM storage again, a sealed backup that cannot be read without the TPM). Other TPMs, firmware TPMs, arm64 and a cleared or replaced TPM are not tested. Try it on a spare machine first.

Moving from the single container

  1. Take a backup with the single-container backup.sh (Backup and Restore), then stop that container.
  2. Point QERTUM_DATA at the same host path and run up -d. The layout is identical, so nothing migrates.
  3. Keep QERTUM_HOST and the ports the same so the issuer and redirect URIs do not change.

Clone this wiki locally