Skip to content
Docs/Operations/Deployment
Edit on GitHub

Deployment

opensecstack supports two primary deployment topologies: a single-host Docker Compose stack (Tier 1) for standard deployments, and a multi-host Kubernetes deployment with optional Helm chart (Tier 2) for elevated or production environments. For environment requirements and toolchain setup, see Installation.

Deployment options

OptionBest forLocation
Docker ComposeLocal dev, NGOs, mid-sized enterprises, research labsdeploy/docker-compose.yml
Kubernetes (plain YAML)Production clusters, multi-region, zero-trust environmentsdeploy/k8s/
Helm chartParameterised production installs, GitOps workflowsdeploy/helm/opensecstack/

Docker Compose (Tier 1)

The deploy/docker-compose.yml file brings up all released platforms on a single Docker network with two segments: frontend (internet-facing services) and backend (databases, caches, internal APIs). TLS terminates at an upstream ingress (Traefik, nginx, or Caddy); inter-service traffic is plain HTTP inside the trusted Docker network.

Bash
"cm"># Copy and populate the environment file
cp deploy/.env.example deploy/.env

"cm"># Start all services
docker compose -f deploy/docker-compose.yml up -d

"cm"># Check health of every container
docker compose -f deploy/docker-compose.yml ps

"cm"># View logs for a specific service
docker compose -f deploy/docker-compose.yml logs -f apiguard-api

"cm"># Stop without destroying data volumes
docker compose -f deploy/docker-compose.yml down

"cm"># Full teardown including volumes (destructive)
docker compose -f deploy/docker-compose.yml down -v

A separate dev override file, deploy/docker-compose.dev.yml, is available for local development with mounted source directories.

Kubernetes / Helm (Tier 2)

For multi-host or zero-trust environments, the Kubernetes manifests in deploy/k8s/ deploy each platform as a Deployment with readiness and liveness probes, ClusterIP services, and nginx Ingress resources.

Bash
"cm"># Apply namespace first
kubectl apply -f deploy/k8s/namespace.yaml

"cm"># Create secrets (never commit real values)
kubectl create secret generic opensecstack-db-secrets \
  --namespace opensecstack \
  --from-literal=POSTGRES_PASSWORD='<strong-password>'

kubectl create secret generic opensecstack-app-secrets \
  --namespace opensecstack \
  --from-literal=APIGUARD_JWT_SECRET='<random-min-32-chars>' \
  --from-literal=NIS2_JWT_SECRET='<random-min-32-chars>' \
  --from-literal=REDIS_PASSWORD='<strong-password>'

"cm"># Apply all manifests recursively
kubectl apply -f deploy/k8s/ -R -n opensecstack

"cm"># Watch pods come up
kubectl get pods -n opensecstack -w

Helm

Bash
cp deploy/helm/opensecstack/values.prod.yaml.example \
   deploy/helm/opensecstack/values.prod.yaml
"cm"># Edit values.prod.yaml — set image tags, replica counts, ingress hosts

helm install opensecstack deploy/helm/opensecstack/ \
  --namespace opensecstack --create-namespace \
  --values deploy/helm/opensecstack/values.prod.yaml

"cm"># Rolling upgrade
helm upgrade opensecstack deploy/helm/opensecstack/ \
  --namespace opensecstack \
  --values deploy/helm/opensecstack/values.prod.yaml
Note: Ingress hostnames default to apiguard.opensecstack.local and nis2compass.opensecstack.local. Update the host: field in each ingress manifest before applying to production.

Port matrix

All API services expose /health and /metrics on their main port.

PlatformAPI portDashboardNotes
APIGuard80803000Public-facing scanning engine
NIS2 Compass80903001Python + Flask
ThreatFlow8091—Threat intelligence (STIX 2.1)
IRFlow8083—Incident response orchestrator
SIN Community8089—Developer knowledge hub; Meilisearch on :7700 (internal)
CITADEL8099—Internal-only; governance engine
sinauth81005173OAuth 2.0 / OIDC identity provider
VertGuard80913009AI-attack defence; ML gRPC side-car on :50051 (internal)
OpenCSIRT80883088Python advisory sub-system on :8089 (internal)
OpenScrub80873087XDP/eBPF DDoS mitigation; hostNetwork on edge nodes
PostgreSQL5432—One DB instance per platform for isolation
Redis6379—Queue and cache (internal)

Environment and secrets

All runtime configuration is driven by environment variables. The canonical template is deploy/.env.example. Copy it to deploy/.env before starting the stack and fill in every required value.

Key variables to set for a minimal deployment:

deploy/.env (excerpt)
Bash
"cm"># Database
POSTGRES_PASSWORD=<strong-password>
NIS2_DB_PASSWORD=<strong-password>
SIN_DB_PASSWORD=<strong-password>
SINAUTH_DB_PASSWORD=<strong-password>

"cm"># Redis
REDIS_PASSWORD=<strong-password>

"cm"># JWT signing secrets (generate with: openssl rand -hex 32)
APIGUARD_JWT_SECRET=<random-min-32-chars>
NIS2_JWT_SECRET=<random-min-32-chars>
NIS2_SECRET_KEY=<random-min-32-chars>
COMMUNITY_JWT_SECRET=<random-min-32-chars>
COMMUNITY_PASSWORD_PEPPER=<random-min-32-chars>

"cm"># CITADEL HMAC secrets
CITADEL_API_KEY=<random-min-32-chars>
IRFLOW_CITADEL_KEY_SECRET=<random-min-32-chars>
IRFLOW_WEBHOOK_APIGUARD_SECRET=<random-min-32-chars>
IRFLOW_WEBHOOK_CITADEL_SECRET=<random-min-32-chars>
IRFLOW_WEBHOOK_THREATFLOW_SECRET=<random-min-32-chars>

"cm"># Meilisearch
MEILI_MASTER_KEY=<random-min-32-chars>

"cm"># sinauth (OIDC issuer URLs)
SINAUTH_ISSUER=https://auth.example.com
SINAUTH_SITE_URL=https://auth.example.com
Warning: Never commit deploy/.env to source control. For production, replace env var injection with a secret manager (HashiCorp Vault, AWS Secrets Manager, or GCP Secret Manager) and rotate HMAC secrets quarterly and the CITADEL anchor key yearly. See Security for the full deployment-tier control matrix.

CITADEL wiring

CITADEL is the governance layer and is intentionally deployed separately — its compose file lives under .citadel/docker-compose.yml. Every platform that emits governance events must be pointed at the CITADEL API:

docker-compose.yml (apiguard-api excerpt)
YAML
environment: - APIGUARD_CITADEL_URL=http://citadel-api:8099
  - APIGUARD_CITADEL_API_KEY=${CITADEL_API_KEY:-}

When running the full stack with both compose files on the same Docker network, use the service hostname citadel-api and port 8099 as the base URL. All platform-to-CITADEL calls are HMAC-SHA256 signed with the shared CITADEL_API_KEY secret and include a ±5-minute replay window.

If CITADEL is unreachable, all MARSHAL-gated actions across the ecosystem are blocked and platforms return HTTP 503 to callers — they never silently proceed.

Network zones

ZoneContainsAccess
PublicIngress / WAF, dashboards (APIGuard UI, NIS2 Compass UI)Internet-facing
ApplicationPlatform API servers (ports 8080–8100)Ingress + internal services only
GovernanceCITADEL (:8099)Only IRFlow, APIGuard, NIS2 Compass, ThreatFlow
DataPostgreSQL (:5432), Redis (:6379)Owning platform only
ObservabilityPrometheus, Grafana, JaegerInternal ops network only

For Tier 2 multi-host deployments, a service mesh (Istio or Linkerd) enforces mTLS between every pair of platforms, and CITADEL runs in active/passive HA with an external leader lock (Consul Lease or Kubernetes Lease) to keep the WORM chain single-writer.

SIN — Security Intelligence Network — Open-source cybersecurity & compliance for the EU Digital Decade.

Apache-2.0 & AGPL-3.0 · GitHub · Contribute