Skip to content

Docker en

coolstartnow edited this page Aug 30, 2026 · 1 revision

Docker: Using the image & environment variables

🇩🇪 Deutsche Version

The published image

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:3000

Or 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:latest

This 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 coolstartnow

Using PostgreSQL or MariaDB instead of JSON

The 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:latest

For 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.

Environment variable reference

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.

How does the database schema get created?

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).

Building from source (optional)

Uncomment the build: block in docker-compose.yml and run docker compose up -d --build.

Why not just build it yourself?

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:

  1. 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.
  2. Trust in the build process. A Dockerfile pulls 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).
  3. Reproducibility. A self-built image can drift unpredictably from the official one due to network timeouts, moving latest tags, or corporate proxies. A GHCR image is a known, fixed state.
  4. Docker's default expectation. "Pull and run" is the normal case with Docker, not "clone and build".
  5. 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.

Clone this wiki locally