-
Notifications
You must be signed in to change notification settings - Fork 0
Deployment
NSC needs:
- the backend and one or more scanner containers;
- PostgreSQL 16 or newer;
- an OIDC provider for production authentication;
- an S3-compatible object store if raw scan-output and execution-log archival is required; and
- network paths from the backend to every scanner, and from each scanner to its assigned targets.
The scanner holds no database credentials. Traffic is backend → scanner only; scanner nodes never register with or call the backend. Keep scanner egress restricted to the networks each node is intended to scan.
cp .env.example .env
# Change SCANNER_TOKEN. Keep the seeded OIDC secret unless you update the realm too.
docker compose up --buildCompose passes VERSION_TAG and GIT_COMMIT into the backend image. Leave
VERSION_TAG unset locally so the account menu shows dev plus the commit. When
GIT_COMMIT is unset, the image reads this checkout's HEAD. Set it when the tree
has no checkout identity (a linked worktree, for example):
GIT_COMMIT=$(git rev-parse HEAD) docker compose up --buildThe seeded Keycloak client secret in deploy/keycloak/realm-nsc.json matches .env.example.
For local development, either leave that development-only OIDC_CLIENT_SECRET unchanged or,
before Keycloak first imports the realm, set the same replacement value in both .env and the
realm JSON. Changing only .env makes the OIDC callback's token exchange fail. If Keycloak already
imported the realm, recreate its Compose container after synchronizing both values (docker compose down, then docker compose up --build); local Keycloak data lives in the container layer.
Open http://localhost:8080. The compose stack includes Postgres, Garage, Keycloak, one scanner,
and the backend/SPA. http://127.0.0.1:8080 redirects to localhost before OIDC starts (the seeded
APP_BASE_URL and Keycloak redirect URI are localhost). Demo users use their username as the password:
| User | Role |
|---|---|
admin |
admin |
operator |
operator |
viewer |
viewer |
Keycloak's local admin console is at http://localhost:8082 (admin / admin). These seeded
credentials and the compose defaults are for local development only.
Raw scan output is archived to Garage (dxflrs/garage, S3 API on port 3900). The image tag
defaults to v2.3.0 and can be overridden with GARAGE_VERSION (see
Configuration); dxflrs/garage publishes no latest tag. The image is
the unmodified upstream build and is AGPL-3.0. Running it for local development or self-hosting
does not change NSC's license. If a deployment modifies Garage and distributes that build, AGPL-3.0
requires publishing the corresponding Garage source; prefer staying on the pinned upstream image
unless that obligation is acceptable. Garage's admin API is bound to loopback inside the container.
deploy/garage/garage.toml sets s3_region to us-east-1 so it matches the backend's S3_REGION.
The bucket nuclei-raw and its access key are created on startup from the GARAGE_DEFAULT_*
variables in docker-compose.yml, which are the same development-only values the backend uses.
A fresh backend creates schema_migrations, applies the current schema baseline, seeds the configured
default scanner node, and starts the scheduler, template sync/distribution, node-health monitor, and
retention sweeper.
Each v* git tag publishes public multi-arch images to GHCR. Anonymous pull works; no
docker login is required:
# Replace with a tag from https://github.com/Nikolasel/nuclei-security-center/releases
# Git tag v0.4.2-beta → image tag 0.4.2-beta (the leading v is stripped).
VERSION=0.4.2-beta
docker pull ghcr.io/nikolasel/nuclei-security-center-backend:$VERSION
docker pull ghcr.io/nikolasel/nuclei-security-center-scanner:$VERSIONCoordinates:
ghcr.io/nikolasel/nuclei-security-center-backend:<version>ghcr.io/nikolasel/nuclei-security-center-scanner:<version>
GHCR package names are lowercase.
To run those images with this repo's Compose stack (Postgres, Garage, Keycloak), edit
docker-compose.yml in place: on the backend and scanner services, delete the
build: mapping and set image: instead. Leave environment, ports, volumes, and
the backend's depends_on (postgres/keycloak health, scanner, garage) in place:
# backend — delete `build:`, set:
image: ghcr.io/nikolasel/nuclei-security-center-backend:0.4.2-beta
# scanner — delete `build:`, set:
image: ghcr.io/nikolasel/nuclei-security-center-scanner:0.4.2-betaThat YAML is not a drop-in second Compose file. docker-compose.override.yml (or a
second -f) merges mappings and would keep build: from docker-compose.yml; an
override must also set build: !reset on both services.
Then docker compose up without --build. Leaving build: in place, or passing
--build, compiles from this tree and retags over the pulled image.
Every tag publishes the full semver (no leading v) and a sha-<short> git SHA. A
non-prerelease also publishes a floating major.minor tag and latest. Prereleases such
as 0.4.2-beta get neither, so docker pull …:latest and …:0.4 404 today. The GHCR
package UI labels the most recently published tag as “Latest”; that is not a :latest
image tag.
After sign-in, the account menu and GET /api/version report the build that is actually
running. A tagged image shows the git tag plus commit (v0.4.2-beta (3beec52)); an untagged
build shows dev plus the commit. Include that string when reporting a problem. See
Troubleshooting.
- Provision an empty PostgreSQL database and a bucket (optional but recommended).
- Deploy one scanner per reachable network zone from
ghcr.io/nikolasel/nuclei-security-center-scanner:<version>(see Run from published images). Give every scanner a strong, distinct bearer token and TLS; use mTLS for untrusted segments. - Deploy
ghcr.io/nikolasel/nuclei-security-center-backend:<version>with Postgres, OIDC, object-store, and initial scanner seed configuration. - Terminate browser TLS at the backend or an ingress and keep
COOKIE_SECURE=true. - Plan for all existing browser sessions to require sign-in again when this session-hardening
release is deployed: old session rows are not converted, and secure deployments also change the
cookie name to the host-locked
__Host-form. - Send backend stdout to the platform log aggregator; that is the audit trail.
- Verify
/healthz, sign in, and complete the first-run bootstrap.
The published images are multi-architecture (linux/amd64, linux/arm64) Red Hat UBI 10 Micro
images. The scanner image contains pinned, checksum-verified nuclei and naabu binaries;
scanner upgrades are image upgrades, not in-place binary updates.
Published from docs/admin/ in the repository. Edit those files in a pull request; this wiki is overwritten on every push to main and on every v* version tag.
Administration
- Home
- Deployment
- Configuration
- Authentication
- First-run bootstrap
- Operations
- Findings and data
- Troubleshooting
In the repository