Run OpenSourceOM with Docker Compose
docker-compose.yml in the core repository starts two services: Postgres 16 and
an API image built from the local Dockerfile. The image entrypoint is
om and the command is serve.
cp .env.example .env
docker compose up -d
docker compose ps
Postgres listens on host port 5432 with user, password, and database opensourceom
unless you override POSTGRES_USER, POSTGRES_PASSWORD, and
POSTGRES_DB. The API listens on OM_API_PORT (default 8080) and
receives OM_ADMIN_EMAIL and the optional Google, GitHub, and SMTP settings from
.env. Open the console and create the admin, or run
om admin create --email on the host first. Keep port 8080 private until an
admin exists. A volume named
postgres-data keeps the database across restarts.
Migrations
om serve applies pending SQL files in migrations/ before it
listens. Compose starts the API with that command, so docker compose up -d
creates the schema. A later image applies only the files that are not already recorded in
schema_migrations. Startup logs applied database migrations or
database schema is current.
Set OM_AUTO_MIGRATE=false in .env when a separate step should own
the schema. om migrate is also how you migrate when Compose is running Postgres
alone:
docker compose up -d postgres
go build -o om ./cmd/om
./om migrate
The CLI’s default database host is localhost, which is the published port.
.env.example sets POSTGRES_HOST=postgres, and the API service in
the compose file sets that same value on the container. The CLI does not read
.env. Do not export POSTGRES_HOST=postgres into the shell that
runs om on your machine.
CLI against Compose
Build om on the host (or unpack a release binary) and use it for scans. The
container image can also run one-off commands if you point it at the Compose network:
docker compose run --rm api migrate api migrate works because the image entrypoint is already om.
Cloud scans are easier on the host, where your AWS, Azure, and GCP credential files already
live. The API container does not mount those credentials. To scan from the container you
would pass the credential environment yourself, for example
-e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY, and the database
host inside that network is postgres, which the service already sets.
If the CLI cannot connect
POSTGRES_HOST=postgresin the shell is the usual miss. That hostname exists only on the Compose network. Unset it, or setPOSTGRES_HOST=localhost, for a CLI on the host.- Password, user, and database must match what Compose used to initialize the volume. Changing
POSTGRES_PASSWORDin.envafter the first start does not alter an existing volume. - The API waits until Postgres is healthy, then applies migrations.
om migrateon the host does not wait. If it runs before the port is open, retry it. relation "nodes" does not existmeans the API has not finished starting, orOM_AUTO_MIGRATE=falseandom migratehas not been applied to this database.
GET http://localhost:8080/v1/health returns
{"status":"ok","service":"opensourceom-api"} when the API process is up and
Postgres answers a ping. om serve applies migrations before it listens, so a
healthy API has the current schema when OM_AUTO_MIGRATE is left on. A scan that
fails on the database while health is ok means the CLI and the container are not using the
same connection settings.
Useful commands
docker compose logs -f api
docker compose down
docker compose down -v down -v deletes the Postgres volume. The next API start is an empty database
and applies migrations again.
Copyright © 2026 OpenSourceOM. Licensed under Apache-2.0.