> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oasm.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment

> Deploy OASM in production from the oasm-docker repository — prerequisites, .env configuration, scaling, updates, and best practices

Production deployments run from the dedicated deployment repository [`oasm-platform/oasm-docker`](https://github.com/oasm-platform/oasm-docker). It packages the platform as ready-to-run container images — `oasm/oasm-console`, `oasm/oasm-api`, and `oasm/oasm-worker` — together with a compose file, a `Makefile`, an nginx configuration, and a `.env.example` that wires the whole stack together.

The two repositories have different roles:

* [`oasm-platform/open-asm`](https://github.com/oasm-platform/open-asm) is the **source-code monorepo** where the platform is developed. It is for building and running from source — see the [Developer guide](/developer-guide).
* [`oasm-platform/oasm-docker`](https://github.com/oasm-platform/oasm-docker) is the **deployment repository** used to run the platform in production. Clone this repo, configure `.env`, and start the stack with `make`.

Deploying production from `open-asm` — cloning the dev repository and copying its compose file — is not the supported path. Always deploy from `oasm-docker`, which pins the runtime configuration, nginx routing, and image tags for production.

## Prerequisites

* **Docker Engine** with **Compose V2** — included with Docker Desktop on Windows and macOS; install the standalone plugin on Linux servers. See the [Docker installation guide](https://docs.docker.com/engine/install/).
* **Make** — required: the repository's `Makefile` drives pull, run, update, and cleanup.
  * **Windows** — use [Git Bash](https://gitforwindows.org/) or [WSL](https://learn.microsoft.com/en-us/windows/wsl/), or install `make` via [Chocolatey](https://chocolatey.org/) (`choco install make`).
  * **macOS** — `brew install make`.
  * **Linux** — `sudo apt install make` (Debian/Ubuntu) or `sudo yum install make` (RHEL-family).
* **Resources** — at minimum **4 CPU cores, 4 GB RAM, and 20 GB free disk space**. Scanning workloads benefit from more.

<Info>
  The stack runs eight services: `console`, `core-api`, `migration`, `oasm-worker`, `postgres`, `redis`, `geo-ip-database`, and `rustfs`. Give the stack a few minutes on first start while images are pulled and the database is initialized.
</Info>

## Deployment stack overview

<Table>
  | Service           | Role                         | Ports                                     | Notes                                                                                                                                                                                                                                        |
  | ----------------- | ---------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `console`         | nginx serving the SPA        | `6276` (HTTP), `16276` (gRPC passthrough) | Mounts `./nginx.conf`; `location /api/` proxies to `core-api`; depends on `core-api` being healthy                                                                                                                                           |
  | `core-api`        | NestJS API — REST + gRPC     | internal (see `PORT`)                     | `PORT` is configurable via `.env` (compose default `6276`; `.env.example` ships `PORT=6277` and the nginx upstream targets `core-api:6277`); writes scan artifacts to `rustfs`; healthcheck `curl http://localhost:${PORT:-6276}/api/health` |
  | `migration`       | One-shot database migration  | —                                         | Runs `npx typeorm migration:run -d dist/database/database-config.js`, then exits (`restart: 'no'`); completes before `core-api` starts                                                                                                       |
  | `oasm-worker`     | Job execution agent          | —                                         | Connects to `core-api` over gRPC (`WORKER_GRPC_HOST=core-api`, `WORKER_GRPC_PORT=16276`); `WORKER_API_KEY=${OASM_CLOUD_APIKEY}`; `WORKER_MAX_CONCURRENCY=10`; 3 replicas by default; volume `/app/oasm-tools` (tool templates)               |
  | `postgres`        | Primary database             | `5432` (internal)                         | `postgres:17`; volume `pgdata`                                                                                                                                                                                                               |
  | `redis`           | Queue and cache              | `6379` (internal)                         | Password via `requirepass ${REDIS_PASSWORD}`; append-only persistence; volume `redis-data`; CPU/memory limits                                                                                                                                |
  | `geo-ip-database` | Geo IP enrichment            | `4360` (internal)                         | Image `ghcr.io/l1ttps/geoip-proxy`; volume `geoip-data`                                                                                                                                                                                      |
  | `rustfs`          | S3-compatible object storage | `9000` (internal)                         | Image `rustfs/rustfs`; stores scan artifacts; volume `rustfs-data`                                                                                                                                                                           |
</Table>

## Deploy with Docker Compose

<Steps>
  <Step title="Clone the deployment repository">
    ```bash theme={null}
    git clone https://github.com/oasm-platform/oasm-docker.git
    cd oasm-docker
    ```
  </Step>

  <Step title="Prepare the environment file">
    ```bash theme={null}
    cp .env.example .env
    ```
  </Step>

  <Step title="Edit .env — set your secrets">
    Replace every default credential before going live:

    * `OASM_CLOUD_APIKEY` — set to your OASM Cloud API key (default `change_me`).
    * `POSTGRES_PASSWORD` — replace the default.
    * `REDIS_PASSWORD` — replace the default, and mirror the new value in `REDIS_URL` (`redis://:PASSWORD@redis:6379/0`).
    * `ENCRYPTION_KEYS` — replace the default `super_secret_key`.

    Optionally pin `IMAGE_TAG` to a specific release (for example `v1.2.0`) instead of `latest` so upgrades are deliberate. Keep the file out of version control.
  </Step>

  <Step title="Start the stack">
    ```bash theme={null}
    make
    ```

    The default target pulls the latest images and starts the stack. The equivalent manual commands are:

    ```bash theme={null}
    docker compose pull
    docker compose up -d
    ```
  </Step>

  <Step title="Wait for core-api to become healthy">
    The console depends on `core-api` being healthy, so it may take a moment to come online. Check the API health endpoint:

    ```bash theme={null}
    curl http://localhost:6276/api/health
    ```

    The console's nginx routes `/api/` to `core-api`, so this host port works even though the API container listens on the internal `PORT`. When it returns a healthy response, the stack is ready.
  </Step>

  <Step title="Open the console and bootstrap the admin">
    Open **[http://localhost:6276](http://localhost:6276)** in your browser. On first run, use the `/init-admin` route to bootstrap the initial admin account before signing in.
  </Step>
</Steps>

<Danger>
  Running with the default secrets (`change_me`, the sample PostgreSQL and Redis passwords) exposes your deployment to credential abuse. Rotate every default before exposing the stack beyond localhost.
</Danger>

### First-run admin setup

OASM exposes the `/init-admin` route for the initial admin bootstrap. The first account created through it receives **admin** privileges automatically. After the admin exists, invite additional members through the members flow — see [Members](/members).

### Verify the deployment

After the stack is up, confirm each layer is healthy before relying on it:

1. **API health** — `curl http://localhost:6276/api/health` returns a healthy response when the Core API is ready.
2. **Console** — open [http://localhost:6276](http://localhost:6276); you should reach the login page and, on first run, the admin bootstrap flow.
3. **Workers** — open the Workers section of the console: the `oasm-worker` instances (3 replicas by default) should appear connected.
4. **Job execution** — enqueue a small discovery job and watch it move from Queued to Completed in the job registry. This proves the full path from API to worker to tools.

These four checks cover the whole data path, so a deployment that passes them is ready for real scanning workloads.

## Port reference

<Table>
  | Port    | Service           | Purpose                                                                          |
  | ------- | ----------------- | -------------------------------------------------------------------------------- |
  | `6276`  | `console` (nginx) | Web UI and `/api/` REST proxy ([http://localhost:6276](http://localhost:6276))   |
  | `16276` | `console` (nginx) | gRPC passthrough to `core-api` — external workers connect here from the internet |
  | `5432`  | `postgres`        | Internal only — database                                                         |
  | `6379`  | `redis`           | Internal only — queue and cache                                                  |
  | `4360`  | `geo-ip-database` | Internal only — Geo IP enrichment                                                |
  | `9000`  | `rustfs`          | Internal only — S3-compatible object storage                                     |
</Table>

<Info>
  In the production `oasm-docker` stack the web console is on **6276**. In the `open-asm` dev source tree the compose file maps the dev console to **3000** and the Vite dev server serves on **5173**; neither applies to production. Keep the two deployment modes separate when troubleshooting.
</Info>

## Scaling workers

Workers connect to the Core API over gRPC and scale horizontally:

* **Default replicas** — the `oasm-worker` service declares `deploy.replicas: 3`; compose starts three workers by default.

* **Add more replicas** — scale the service to any count:

  ```bash theme={null}
  docker compose up -d --scale oasm-worker=5
  ```

* **Per-worker concurrency** — `WORKER_MAX_CONCURRENCY` (default `10`) in the `oasm-worker` service environment limits how many jobs a single worker executes at once. Raise it for faster throughput on capable hosts, lower it to reduce load.

* **Scope awareness** — a worker runs under **Workspace** or **Global** scope, which determines which jobs it may pull. See [Workers](/worker).

Workers cache installed tool templates in the `/app/oasm-tools` volume, so tool installation happens once per worker environment rather than once per job.

## Updates

* **`make update`** — pulls the latest commit and images, then restarts the stack.
* **`make update-main`** — refreshes the images and restarts the stack without pulling a new commit.

Migrations run automatically: the one-shot `migration` service applies TypeORM migrations before `core-api` starts (its `depends_on` requires `service_completed_successfully`), so upgrading the images applies schema changes in order. Watch the `migration` service log to confirm migrations applied, and check release notes for any manual steps before upgrading major versions.

## Storage considerations

<Table>
  | Volume            | Service           | Contents                                                                                                                 |
  | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
  | `pgdata`          | `postgres`        | **Source of truth** — assets, scan results, vulnerabilities, and system state. Back it up and keep it on durable storage |
  | `redis-data`      | `redis`           | Queue state (append-only persistence)                                                                                    |
  | `geoip-data`      | `geo-ip-database` | GeoIP database files                                                                                                     |
  | `rustfs-data`     | `rustfs`          | Scan artifacts (screenshots, scan outputs) written by `core-api`                                                         |
  | `/app/oasm-tools` | `oasm-worker`     | Cached worker tool templates                                                                                             |
</Table>

`rustfs` is the stack's S3-compatible object store, reachable by the API at `http://rustfs:9000` (internal). `core-api` writes scan artifacts there; they are stored in the `rustfs-data` volume. Losing it loses enrichment data, but the inventory itself survives; artifacts can be regenerated by rescanning. All other services are stateless or rebuildable, so the stack can be recreated from the compose files without data loss.

## Environment variables reference

All variables below come from the `.env.example` shipped with `oasm-docker`; defaults are shown.

<Table>
  | Variable            | Default                          | Purpose                                                                                                                                    |
  | ------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
  | `IMAGE_TAG`         | `latest`                         | Docker image tag for all `oasm/*` images; pin to a specific release for production                                                         |
  | `OASM_CLOUD_APIKEY` | `change_me`                      | API key for OASM Cloud integration                                                                                                         |
  | `POSTGRES_HOST`     | `postgres`                       | PostgreSQL host                                                                                                                            |
  | `POSTGRES_USERNAME` | `postgres`                       | PostgreSQL user                                                                                                                            |
  | `POSTGRES_PASSWORD` | `postgres`                       | PostgreSQL password — replace in production                                                                                                |
  | `POSTGRES_PORT`     | `5432`                           | PostgreSQL port                                                                                                                            |
  | `POSTGRES_DB`       | `open_asm`                       | Main database name                                                                                                                         |
  | `POSTGRES_SSL`      | `false`                          | Enable SSL for the main database connection                                                                                                |
  | `PORT`              | `6277`                           | Internal port the core API listens on; compose default is `6276` and the nginx upstream targets `core-api:6277` (the `.env.example` value) |
  | `REDIS_URL`         | `redis://:open_asm@redis:6379/0` | Redis connection string — contains the Redis password, keep it in sync with `REDIS_PASSWORD`                                               |
  | `REDIS_PASSWORD`    | `open_asm`                       | Redis password — replace in production                                                                                                     |
  | `ENCRYPTION_KEYS`   | `super_secret_key`               | Data-encryption key — replace in production                                                                                                |
  | `GEO_IP_URL`        | `geo-ip-database:4360`           | Geo IP service endpoint                                                                                                                    |
  | `RUSTFS_ENDPOINT`   | `http://rustfs:9000`             | Object store endpoint used by the core API                                                                                                 |
  | `RUSTFS_ACCESS_KEY` | `rustfsadmin`                    | Object store access key                                                                                                                    |
  | `RUSTFS_SECRET_KEY` | `rustfssecret`                   | Object store secret key — replace in production                                                                                            |
</Table>

## Production checklist

Before exposing a deployment beyond your network, verify each item:

* [ ] Default secrets replaced: `OASM_CLOUD_APIKEY`, `POSTGRES_PASSWORD`, `REDIS_PASSWORD` (mirrored in `REDIS_URL`), `ENCRYPTION_KEYS`, `RUSTFS_SECRET_KEY`
* [ ] `IMAGE_TAG` pinned to a specific release instead of `latest`
* [ ] TLS termination configured in front of **6276**
* [ ] Postgres backups scheduled and a restore tested once
* [ ] Upgrade path rehearsed with `make update`
* [ ] `docker compose ps` shows every service up and the API health endpoint responds

## Best practices

* **Secrets management** — keep credentials in an environment file or secret manager, never commit them. Rotate the database and Redis passwords on a schedule and whenever access may have leaked.
* **TLS termination** — terminate TLS at a reverse proxy (for example nginx, Caddy, or a load balancer) in front of the console on **6276**. The compose stack does not serve HTTPS by itself.
* **Back up PostgreSQL** — schedule regular backups of the `pgdata` volume. It holds assets, scan results, vulnerabilities, and system state — the rest of the stack can be rebuilt, the data cannot. Test a restore into a scratch environment periodically; an untested backup is a guess. Store encrypted backups off the host.
* **Upgrades** — upgrade with `make update` (or `make update-main` to refresh images without pulling a new commit). Migrations apply automatically before the API starts; check release notes for manual steps before major versions.
* **Log retention** — keep compose logs long enough to diagnose failed jobs. A failed scan is only debuggable while its worker and API logs still exist.

## Related

<Card icon="map" title="Onboarding" horizontal href="/onboard">
  Set up your workspace after the first login
</Card>

<Card icon="code" title="Developer guide" horizontal href="/developer-guide">
  Run and develop the platform from the source tree
</Card>

<Card icon="network" title="Architecture" horizontal href="/architecture">
  Understand the distributed system you are deploying
</Card>

<Card icon="siren" title="Troubleshooting" horizontal href="/troubleshooting">
  Resolve common deployment and runtime issues
</Card>
