Configuration¶
The store volume mounts at /var/lib/zeroecho/store-container, with PKI state
under store/ and server-control state under server-control/. The writable
parent also retains cold-restore journals and rollback roots. Existing volumes
need the explicit offline layout migration;
never point a new child configuration at an unmigrated old root.
The PKI server keeps no secrets and no storage locations baked into its image. Everything it needs at run time arrives through three channels: persistent data volumes, read-only configuration mounts, and environment variables for secrets. This page is the contract for all three. The exact field names come from the strict versioned server configuration; the configuration reference documents each field in full.
Volumes¶
Four persistent volumes hold the server's state. The store and keyring are kept on separate volumes so the keyring, which holds private-key material, can be placed on stricter, separately backed storage.
| In-container path | Purpose | Permissions | Persistence guidance |
|---|---|---|---|
/var/lib/zeroecho/store-container |
Main PKI store — authorities, requests, credentials, revocations, the metadata log, staged content, and the control log | dir 0700 |
The system of record. Back it up consistently. Never share it between two running servers. |
/var/lib/zeroecho/keyring |
CA signing keyring (private-key material) | dir 0700, keys 0600 |
A separate, stricter volume from the store. Restrict access and backups; apply separate key management if available. |
/var/lib/zeroecho/artifacts |
Published objects written by the filesystem publisher (CA certificates, leaf certificates, CRLs) | dir 0750 |
Durable but reconstructible — it can be republished from the store. |
/var/lib/zeroecho/audit |
Durable audit sink | dir 0750 |
Retain per your audit-retention policy. Append-heavy. |
The image pre-creates these directories owned by uid 10001 (zeroecho). A fresh,
empty Docker named volume inherits that ownership on first mount. A bind mount does
not — set ownership and permissions on the host path before the first start. On
Kubernetes, the StatefulSet sets fsGroup: 10001 so the claims are group-writable
by the runtime user.
Store and keyring must be POSIX filesystems
The store and keyring depend on POSIX permission enforcement, atomic rename,
directory fsync, and advisory locking. A non-POSIX bind mount silently drops
those guarantees, and the keyring refuses to start without owner-only
permission support. See Requirements.
Backup and restore procedures
The paths and permissions listed here are the source of truth for volume layout. For backup strategies, snapshot guidance, and step-by-step restore and disaster-recovery procedures, see Operations.
Read-only configuration mounts¶
The server's behaviour is driven by files mounted read-only. They are never written back and can be sourced from host files, Docker secrets, or Kubernetes ConfigMaps and Secrets.
| Mount | Source | Purpose |
|---|---|---|
/etc/zeroecho/server-config.json |
operator file or ConfigMap | The strict versioned server configuration. Validated by --validate-config at start-up (fail-fast). |
/etc/zeroecho/profiles/ |
operator directory or ConfigMap | One certificate-profile document per file. Reconciled at start-up. |
| TLS material directory | operator directory or Secret | The server's own PKCS#12 stores, referenced from the server configuration. Mounted at /var/lib/zeroecho/tls/ in the Compose stack and /etc/zeroecho/tls/ on Kubernetes. |
/etc/zeroecho/health/ |
operator directory or Secret | The health-probe client identity used by the container and probe checks. |
The entrypoint's start-up sequence — validate, reconcile profiles, then start the server — is described in Container images.
The server configuration file¶
The server configuration is a single strict versioned JSON document. It contains no secrets — only the names of the environment variables that hold them. In the reference trusted-reverse-proxy topology, its notable sections are:
listener— the admin mutual-TLS listener on0.0.0.0:8443, with atlsProviderthat names its PKCS#12 key store and trust store and the environment variables holding their passwords.clientCertificateRequiredistrue.authentication—mode: TRUSTED_REVERSE_PROXY, the forwarded-certificate format (RFC9440) and header name (Client-Cert), the mapping from the reverse proxy's transport certificate to a trusted-proxy principal, the mapping from the forwarded administrator leaf to an administrative principal, and theadministrativeClientTrustthat validates the forwarded leaf.realm.pkiSession— the store, audit sink, signing workflow (including the keyring path and its unlock environment variable), and the publishers.runtime.keyUnlockEnvironmentVariable— the environment variable that unlocks the signing keyring.
Point every path in the configuration at an in-container mount. The canonical mapping used by the reference artifacts is:
| Configuration field | In-container path |
|---|---|
store root |
/var/lib/zeroecho/store-container/store |
audit root |
/var/lib/zeroecho/audit |
publisher root |
/var/lib/zeroecho/artifacts |
signing keyringPath |
/var/lib/zeroecho/keyring/ca-keyring.zek |
controlLog |
/var/lib/zeroecho/store-container/server-control/server-control.log |
| TLS PKCS#12 stores | /var/lib/zeroecho/tls/... (Compose) or /etc/zeroecho/tls/... (Kubernetes) |
Prefer absolute in-container paths
The server's working directory is /var/lib/zeroecho, so a relative path such
as tls/server-identity.p12 resolves under it. Absolute paths remove the
ambiguity — the Kubernetes ConfigMap uses them throughout.
Preflight validation¶
The entrypoint runs a configuration preflight before anything else:
This parses and fully validates the document without opening providers, listeners, or the store. Run it yourself against a candidate configuration before a deploy to catch mistakes early; a non-zero exit means the server would refuse to start.
Secrets: environment variables only¶
Every password reaches the server only through the process environment. The
server configuration names the variable; the value never appears in the image or in
any file on disk. The variable name must match the pattern [A-Z][A-Z0-9_]{0,127}.
| Variable | Consumed by | Purpose |
|---|---|---|
ZEROECHO_KEYRING_PASSWORD |
runtime.keyUnlockEnvironmentVariable and/or signing unlockEnvironmentVariable |
Unlocks the CA signing keyring. |
ZEROECHO_TLS_KEYSTORE_PASSWORD |
listener tlsProvider.keyStorePasswordEnvironment |
Password for the server's own identity PKCS#12 store. |
ZEROECHO_PROXY_TRUSTSTORE_PASSWORD |
listener tlsProvider.trustStorePasswordEnvironment |
Password for the trust store that admits the reverse proxy's transport certificate. |
ZEROECHO_ADMIN_TRUSTSTORE_PASSWORD |
administrativeClientTrust.trustStorePasswordEnvironment |
Password for the trust store that validates the forwarded administrator leaf. |
ZEROECHO_PUBLISHER_SECRETS_PASSWORD |
runtime.publisherSecretsEnvironmentVariable |
Unlocks the publisher secrets store for CLASSIC and HYBRID protection profiles. |
In Compose these come from deploy/.env; on Kubernetes they come from a Secret
projected with envFrom. The environment template also carries the host paths and
edge settings:
# Secrets (passwords-by-env). Never commit a filled-in .env.
ZEROECHO_KEYRING_PASSWORD=change-me-keyring
ZEROECHO_TLS_KEYSTORE_PASSWORD=change-me-tls-keystore
ZEROECHO_PROXY_TRUSTSTORE_PASSWORD=change-me-proxy-truststore
ZEROECHO_ADMIN_TRUSTSTORE_PASSWORD=change-me-admin-truststore
# Host paths mounted read-only into pki-server.
ZEROECHO_SERVER_CONFIG_FILE=./config/server-config.json
ZEROECHO_PROFILES_HOST_DIR=./config/profiles
ZEROECHO_TLS_HOST_DIR=./tls/server
ZEROECHO_HEALTH_HOST_DIR=./tls/health
# nginx edge.
NGINX_SERVER_NAME=pki.example.com
PKI_PROXY_SSL_NAME=zeroecho-admin
NGINX_EDGE_TLS_DIR=./tls/edge
NGINX_ADMIN_TRUST_DIR=./tls/admin-trust
NGINX_UPSTREAM_TLS_DIR=./tls/upstream
TLS and PKCS#12 material¶
The two containers hold distinct TLS material. Keep the administrators' trust
anchors consistent: the edge's browser-mTLS trust bundle and the server's
administrativeClientTrust must share the same anchors.
| Container | Files | Role |
|---|---|---|
| PKI server TLS dir | server-identity.p12 |
Server certificate and key for the admin listener. |
| PKI server TLS dir | proxy-transport-trust.p12 |
Trusts the edge's transport client certificate (listener client trust). |
| PKI server TLS dir | administrators-trust.p12 |
Trusts the administrator client certificates forwarded by the edge. |
nginx /etc/nginx/tls/edge/ |
edge.crt, edge.key |
The browser-facing server certificate. |
nginx /etc/nginx/tls/admin-trust/ |
admin-ca-bundle.pem |
CA bundle that browser client certificates must chain to. Same anchors as administrators-trust.p12. |
nginx /etc/nginx/tls/upstream/ |
proxy-client.crt, proxy-client.key, pki-server-ca.pem |
The edge's transport client identity for upstream mutual-TLS, and the CA that verifies the server certificate. |
Changing a certificate profile¶
Certificate profiles live in the read-only profiles mount and are reconciled at start-up. To change one:
- Edit or add the profile document in the profiles source — a host directory, or the profiles ConfigMap on Kubernetes.
- Restart the server. The entrypoint registers and activates any profile whose identifier is not already present in the store.
Editing an existing profile requires changed content
The bootstrap skips a profile when its active version already has identical
content (SHA-256 of the canonical document matches); changed content is always
re-registered and activated. Restarting with an unmodified profile document
is a no-op for that profile. To change an active profile, update the document
content in the profiles source before restarting, or register a new version
through the console or the command line. Set
ZEROECHO_PROFILE_RECONCILE=off to disable the start-up reconcile if a
separate process owns profile lifecycle.
Pointing the store at a persistent volume without rebuilding¶
The store location is configuration plus a mounted volume — never baked into the image. Because the configuration uses in-container paths, you can repoint the underlying storage freely without touching the image or the configuration.
The named volume store maps to /var/lib/zeroecho/store-container. To use a specific
host path, replace the named volume with a bind mount — after setting its
ownership to uid 10001 and mode 0700 — or declare the named volume as
external: true. No rebuild is needed; the configuration's store root
already points at /var/lib/zeroecho/store-container/store.
The store volume-claim template binds a PersistentVolumeClaim to
/var/lib/zeroecho/store-container. Set storageClassName to a POSIX class, or
pre-provision the PersistentVolume. The configuration ConfigMap points the
store root at the same in-container path.
The same pattern applies to the keyring, artifacts, and audit volumes.
Signing provider¶
The realm.pkiSession.signing block selects the provider that performs CA signing
operations. The default provider (zeroecho-lib) keeps private keys in the local encrypted
keyring. When keys must stay outside the PKI server process — for HSM integration, a
separate signing node, or an operator-approval flow — use the
remote signing provider instead.
Continue to Kubernetes for a full manifest walkthrough, or to Security hardening for the trust model these mounts implement.