Install a self-hosted security graph
Core runs as two processes that share one Postgres database: the om CLI (scans,
rules, queries) and the API (console and HTTP). Docker Compose starts Postgres and the API.
You build or download the CLI and point it at the published database port.
Prerequisites
- Docker with Compose
- Go 1.27.1 if you build
omfrom source (or a release archive from GitHub Releases) - Cloud credentials only when you scan a real account. The demo graph needs none.
Start Postgres and the API
git clone https://github.com/OpenSourceOM/core.git
cd core
cp .env.example .env
docker compose up -d
Compose publishes Postgres on localhost:5432 and the API on
http://localhost:8080. The API container does not apply
schema migrations. The CLI does.
Compose reads .env for the API container. The CLI does not. It defaults to
POSTGRES_HOST=localhost, which matches the published port. Leave
POSTGRES_HOST=postgres inside .env for the container, and do not
export that value into the shell you use for om.
Build the CLI and migrate
go build -o om ./cmd/om
./om migrate om migrate applies the SQL files in migrations/. Run it from the
repository root so the CLI can find that directory.
Load the sample graph
./om scan demo
./om paths list
./om paths run internet-to-datastore
./om graph stats om scan demo replaces the previous demo account, upserts a small AWS-and-Kubernetes
sample, runs every CSPM rule, and prints one attack path.
What the demo shows
The command replaces account ids 111122223333 and cluster-demo,
then prints an attack path and up to eight findings from those accounts. The path is
Internet → sg-web → web-1 → AdminRole → prod-db. worker-1 has no public IP and
only reaches the private bucket acme-assets through AppRole.
acme-logs-public is a public bucket that is not on the web-1 path. The
Kubernetes sample is a workload named frontend with
k8s_service_type: LoadBalancer and image nginx:1.25.3, reachable
from the internet and not connected to prod-db. web-1 lists
package cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*. worker-1 lists
the same product at 2.17.1. om enrich cve attaches CVE-2021-44228
to web-1 because that package matches.
Open http://localhost:8080. The console opens on
internet-to-datastore, which should draw that path. Stats should be non-zero,
and the findings list should include attack-path rows for web-1 to prod-db, the public datastore,
and the internet-exposed workload. web-1 matches the internet-exposed workload rule, the
public-IP rule, and the IMDSv1 rule, so each of those becomes its own attack-path finding.
The card lists the ids internet:global, aws:sg:sg-web,
aws:ec2:i-web-1, aws:iam:role/AdminRole, and
aws:rds:prod-db.
If the page is empty, the CLI and the API are pointed at different databases. See
Docker Compose.
Scan a cloud account
Collectors use each cloud’s default credential chain. They are read-only toward the account. Export variables in the shell, then scan. Details and IAM scope are on the collector pages.
export AWS_REGION=us-east-1
./om scan aws
export AZURE_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000000
./om scan azure
export GCP_PROJECT_ID=my-project
./om scan gcp
./om scan k8s After a scan, evaluate rules and attach CVEs that match workload inventory:
./om rules run
./om enrich cve
./om identity blast-radius --name AdminRole Next
- Configuration for every variable the CLI reads
- The graph for how nodes and edges are used
- CLI for the full command list
- Kubernetes when you leave Compose
Questions
How do I install OpenSourceOM?
Clone the OpenSourceOM/core repository, start Postgres and the API with Docker Compose, build the om CLI, and run om migrate. Then om scan demo loads a sample graph at http://localhost:8080.
Can I try OpenSourceOM without cloud credentials?
Yes. om scan demo loads a sample environment and runs the CSPM rules. AWS, Azure, GCP, and Kubernetes scans are separate commands and use each cloud’s default credential chain.
Related reading
Copyright © 2026 OpenSourceOM. Licensed under Apache-2.0.