-
Notifications
You must be signed in to change notification settings - Fork 50
Docker en
Every release is published as a GitHub Package in the GitHub Container Registry, for
linux/amd64 and linux/arm64 — tagged :latest and :<version> (e.g. :1.40.0):
docker compose up -d
# App runs at http://localhost:3000Or without Compose (data/ must be a bind mount — data is never baked into the image):
docker run -d --name isms-builder -p 3000:3000 \
-e JWT_SECRET="$(openssl rand -hex 32)" \
-v "$PWD/data:/app/data" \
ghcr.io/coolstartnow/isms-builder:latestThis is the default: data is stored as plain JSON files in ./data — no database
container needed. Recommended setup for small teams.
Signed provenance verification: every image carries a signed build provenance attestation proving it was actually built from this repository via GitHub Actions:
gh attestation verify oci://ghcr.io/coolstartnow/isms-builder:latest --owner coolstartnowThe image supports this out of the box, but needs a second container (the actual
database) plus a handful of environment variables telling the app how to reach it.
Important if you only pulled the image (docker pull ghcr.io/coolstartnow/isms-builder)
without cloning this repository: docker-compose.yml and .env.example, where these
variables are otherwise documented, live in the Git repo, not in the image itself. This
page exists precisely for that reason.
The isms-builder image never bundles a database server itself — postgres:17 (or
mariadb:11) is a completely generic, empty database engine from Docker Hub with zero
knowledge of this project's tables. Those tables (risks, assets, SoA controls, etc.) are
created automatically by the application itself the moment it starts and finds an empty
database — no manual SQL import, no separate migration step.
Two containers, one shared Docker network, then the app is told where to find the database:
# 1) An isolated network so the two containers can reach each other by name
docker network create isms-net
# 2) The database — empty until the app first connects and creates its tables
docker run -d --name isms-postgres --network isms-net \
-e POSTGRES_DB=isms_builder \
-e POSTGRES_USER=isms \
-e POSTGRES_PASSWORD="$(openssl rand -hex 16)" \
-v isms-postgres-data:/var/lib/postgresql/data \
postgres:17-alpine
# 3) The app, pointed at that database by container name via Docker's built-in DNS —
# no manual host/port juggling needed
docker run -d --name isms-builder --network isms-net -p 3000:3000 \
-e JWT_SECRET="$(openssl rand -hex 32)" \
-e STORAGE_BACKEND=postgres \
-e DB_HOST=isms-postgres \
-e DB_PORT=5432 \
-e DB_USER=isms \
-e DB_PASS="<same password as POSTGRES_PASSWORD above>" \
-e DB_NAME=isms_builder \
ghcr.io/coolstartnow/isms-builder:latestFor MariaDB: swap postgres:17-alpine for mariadb:11, use its MARIADB_DATABASE /
MARIADB_USER / MARIADB_PASSWORD variables in step 2, and STORAGE_BACKEND=mariadb /
DB_PORT=3306 in step 3.
Compose users get this for free: docker-compose.yml already ships mariadb and
postgres service profiles (commented out by default, alongside the variable
explanations) — docker compose --profile postgres up -d starts both containers wired
together automatically, no manual networking or password copy-pasting required.
| Variable | Purpose |
|---|---|
JWT_SECRET |
(required) Secret for JWT signing — use 32+ random characters |
PORT |
HTTP/HTTPS listen port, default 3000
|
STORAGE_BACKEND |
json (default), sqlite, postgres/pg, or mariadb
|
DB_HOST |
Hostname of the database container — on a shared network, just its --name
|
DB_PORT |
5432 for PostgreSQL, 3306 for MariaDB |
DB_USER / DB_PASS / DB_NAME
|
Must match whatever you set on the database container |
SSL_CERT_FILE / SSL_KEY_FILE
|
Paths to TLS certificate/key → enables HTTPS |
DATA_DIR |
Override data directory (for Docker volumes), default ./data
|
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM
|
SMTP for email alerts |
The full, cross-referenced list of every environment variable this project
understands (including SSL, reverse-proxy trust, SMTP, 2FA, and more) lives in
.env.example in
the repository.
Neither postgres:17 nor mariadb:11 knows anything about this project — they're
generic, empty database engines. There's no SQL dump to import and no separate migration
command. Instead, the application code itself
(server/db/knexDatabase.js)
creates every table it needs the moment the container starts and finds an empty database
— via hasTable()/createTable(), before the app starts accepting HTTP requests. That
makes it idempotent: the first start builds the full schema, every later restart against
the same database is a silent no-op. Live-verified against SQLite, MariaDB 11, and
PostgreSQL 17 (see #70).
Uncomment the build: block in docker-compose.yml and run
docker compose up -d --build.
Fair question: if building the image yourself only takes ~2 minutes — why publish a ready-made GHCR image at all? The answer: build time is rarely the actual blocker, for several reasons — some of which matter especially for an ISMS/compliance tool:
- Setup time before the build. Someone just evaluating the tool often doesn't have a working Docker build environment ready — that costs more time than the build itself.
-
Trust in the build process. A
Dockerfilepulls base images and packages from the network. Building it yourself means trusting every one of those upstream sources — instead of just the one curated, signed image (see provenance verification above). -
Reproducibility. A self-built image can drift unpredictably from the official one
due to network timeouts, moving
latesttags, or corporate proxies. A GHCR image is a known, fixed state. - Docker's default expectation. "Pull and run" is the normal case with Docker, not "clone and build".
- Compliance/audit context (especially relevant here): in many organisations, developers aren't allowed to just build arbitrary Dockerfiles and pull base images from the network — an approved, signed image from a registry is often the only practical and auditable path. For an ISMS tool that runs in exactly those environments, the signed GHCR image isn't just more convenient — it matches the project's own emphasis on traceable provenance.