Guides
Docker Deployment
Run uPKI CA in Docker or Docker Compose with production-ready settings.
Docker Deployment
Single container
docker run -d \
--name upki-ca \
-p 5000:5000 \
-p 5001:5001 \
-e UPKI_DATA_DIR=/data \
-e UPKI_CA_SEED="${PKI_SEED}" \
-e UPKI_CA_HOST=0.0.0.0 \
-v upki-ca-data:/data \
ghcr.io/circle-rd/upki-ca:latest
On the very first start, the container runs init automatically (creating the CA key and certificate), then starts both ZMQ listeners.
Docker Compose with uPKI RA
# docker-compose.yml
services:
upki-ca:
image: ghcr.io/circle-rd/upki-ca:latest
restart: unless-stopped
environment:
UPKI_DATA_DIR: /data
UPKI_CA_SEED: ${PKI_SEED}
UPKI_CA_HOST: 0.0.0.0
volumes:
- upki-ca-data:/data
ports:
- "5000:5000"
- "5001:5001"
healthcheck:
test:
- "CMD-SHELL"
- >
python -c "import socket; s=socket.socket(); s.settimeout(2);
s.connect(('127.0.0.1', 5000)); s.close()"
interval: 10s
timeout: 5s
retries: 10
start_period: 15s
upki-ra:
image: ghcr.io/circle-rd/upki-ra:latest
restart: unless-stopped
depends_on:
upki-ca:
condition: service_healthy
environment:
UPKI_DATA_DIR: /data
UPKI_CA_HOST: upki-ca
UPKI_CA_SEED: ${PKI_SEED}
UPKI_RA_TLS: "true"
UPKI_RA_SANS: "upki-ra"
volumes:
- upki-ra-data:/data
ports:
- "8000:8000"
volumes:
upki-ca-data:
upki-ra-data:
.env file:
PKI_SEED=your-strong-random-seed-here
Healthcheck
The default CA healthcheck uses a TCP socket probe on port 5000 — it does not require the CA to be in a particular operational state, just to be listening.
healthcheck:
test:
- "CMD-SHELL"
- >
python -c "import socket; s=socket.socket(); s.settimeout(2);
s.connect(('127.0.0.1', 5000)); s.close()"
interval: 10s
timeout: 5s
retries: 10
start_period: 15s
Persisting data
Always mount UPKI_DATA_DIR as a named volume or host bind mount. The CA root key, all node certificates, and the TinyDB files live there.
If
UPKI_DATA_DIR is not persisted, the CA will regenerate a new root key on every container restart, invalidating all previously issued certificates.Exposing ZMQ ports
In production:
- Port 5000 — expose only to RA nodes and admin tools. Do not expose to the internet.
- Port 5001 — expose only during initial RA registration, then restrict with a firewall rule.
Use Docker network isolation or a firewall to enforce this:
networks:
pki-internal:
internal: true # no external internet routing