Bulk Signer - Setup on Docker
The provided Dockerfile produces a Debian-slim image. Alpine is intentionally not used because Pkcs11Interop (used by Lacuna.Pki.Pkcs11) and most HSM .so libraries are not built for musl, and System.Security.Cryptography.Xml has historical quirks on musl that affect XAdES signing.
Build
docker build -t lacuna-bulksigner:v2 .
Run with docker compose
Create a .env next to the docker-compose.yml:
BULKSIGNER_API_KEY=your-api-key
BULKSIGNER_PFX_PASSWORD=your-pfx-password
LACUNA_PKI_LICENSE=<lacuna-license-string>
# BULKSIGNER_HSM_PIN=... # only if CertificateSource = Pkcs11
# BULKSIGNER_TSA_PASSWORD=... # only if TSA needs auth
Then:
docker compose up -d --build
docker compose logs -f bulksigner
Host-side data lives in:
docker-data/{input,processing,output,error,logs,db}
docker-config/certificates/ # mount your signer.pfx here, read-only
./docker-data/ is .gitignored. Drop a file into docker-data/input/ to exercise the watcher.
Volumes
The compose file maps each subfolder of /var/bulksigner/ to a host path under ./docker-data/, plus a read-only certificates mount. Customise as needed.
Healthcheck
The container's healthcheck hits http://localhost:8080/api/health every 30 s. The endpoint is anonymous and cached for 5 s.
Sizing
Sequential signing is the bottleneck; a single replica handles the configured workload. Don't run multiple replicas against the same folder/database — this is explicitly disallowed in v1.
Production deployment
- Place the container behind a reverse proxy that terminates TLS (Nginx, Traefik, cloud LB). The app does not terminate TLS itself in v1.
- Mount real volumes (not bind mounts) for
output/,error/, anddb/in production. - Set
Signing__LicenseandBULKSIGNER_API_KEYvia the orchestrator's secret mechanism — never viadocker run -e.
See also
- Configuration — full env-var checklist
- Troubleshooting