Skip to main content

Getting started

Deploy the stack with the prebuilt images published to GHCR — no clone, no build. You only need Docker and two files: a compose.yaml and a .env.

Prerequisites

  • Docker Engine 23+ with the Compose v2 plugin.
  • A hostname pointing at the machine and TLS certificates for it (or self-signed for a local test).
  • Ports 80 and 443 free (configurable — see TLS & networking).

1. Get the compose file and env template

Pick any directory you like — /opt/ocsinventory is a common choice. Download the ready-to-use bundle from the repository's examples/ folder:

sudo mkdir -p /opt/ocsinventory && cd /opt/ocsinventory
curl -fsSL -O https://raw.githubusercontent.com/vdeville/ocsinventory-docker/main/examples/compose.yaml
curl -fsSL -o .env https://raw.githubusercontent.com/vdeville/ocsinventory-docker/main/examples/.env.example
View compose.yaml
# OCS Inventory 3.0.0 — production stack (PostgreSQL, gunicorn, nginx edge).
# Uses prebuilt images from GHCR.
# To build from source, add the dev override:
# docker compose -f compose.yaml -f compose.dev.yaml build
#
# cp .env.example .env # then fill it in
# docker compose pull && docker compose up -d

x-backend-common: &backend-common
image: ghcr.io/vdeville/ocsinventory-backend:${OCS_BACKEND_REF:-3.0.0-rc1}
env_file: .env
volumes:
# Uploaded media (deployment packages, filemanager); read-only in frontend.
- ocs-media:/app/media
depends_on:
db:
condition: service_healthy
restart: unless-stopped

services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ocs-pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped

# One-shot: applies migrations (+ optional admin password).
# Backend and automation wait for it to complete successfully.
ocs-init:
<<: *backend-common
environment:
OCS_ROLE: init
healthcheck:
disable: true
restart: "no"

backend:
<<: *backend-common
environment:
OCS_ROLE: web
depends_on:
db:
condition: service_healthy
ocs-init:
condition: service_completed_successfully
expose:
- "8000"

automation:
<<: *backend-common
environment:
OCS_ROLE: automation
healthcheck:
disable: true
depends_on:
db:
condition: service_healthy
ocs-init:
condition: service_completed_successfully
command:
- sh
- -c
- 'while true; do python manage.py automation --loglevel "$${LOG_LEVEL:-INFO}" || true; sleep "$${AUTOMATION_INTERVAL:-60}"; done'

frontend:
image: ghcr.io/vdeville/ocsinventory-frontend:${OCS_FRONTEND_REF:-3.0.0-rc1}
environment:
# BACKEND_API_ROUTE is optional: if empty it is derived from PUBLIC_URL + API_BASE_PATH at startup so the prefix can't desync.
PUBLIC_URL: ${PUBLIC_URL:-}
BACKEND_API_ROUTE: ${BACKEND_API_ROUTE:-}
FRONTEND_BASE_PATH: ${FRONTEND_BASE_PATH:-/front/}
API_BASE_PATH: ${API_BASE_PATH:-/api/}
# Ports nginx listens on (also the public ports); used to build the HTTP->HTTPS redirect.
HTTP_PORT: ${HTTP_PORT:-80}
HTTPS_PORT: ${HTTPS_PORT:-443}
# Backend upstream for the API proxy.
# BACKEND_RESOLVER enables runtime DNS re-resolution of the upstream; leave empty for a static address.
BACKEND_UPSTREAM: ${BACKEND_UPSTREAM:-backend:8000}
BACKEND_RESOLVER: ${BACKEND_RESOLVER-127.0.0.11}
ports:
- "${HTTP_PORT:-80}:${HTTP_PORT:-80}"
- "${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
volumes:
- ./certs:/etc/nginx/certs:ro
- ocs-media:/media:ro
depends_on:
- backend
restart: unless-stopped

volumes:
ocs-pgdata:
ocs-media:

2. Fill in .env

Edit .env and set at least these. The full list is in Environment variables.

VariableWhat to set
PUBLIC_URLYour public base URL, e.g. https://ocs.example.com.
SECRET_KEYA stable random key (generate it, see below).
DB_PASSWORDA strong database password.
OCS_ADMIN_PASSWORDInitial password for the admin account.
ALLOWED_HOSTSYour hostname, e.g. ocs.example.com.
CSRF_TRUSTED_ORIGINSYour origin, e.g. https://ocs.example.com.

Generate a secret key once and keep it stable:

openssl rand -hex 50

BACKEND_API_ROUTE and FRONTEND_REDIRECT are derived from PUBLIC_URL when left empty — you normally don't set them.

View the full .env template
# ============================================================================
# OCS Inventory 3.0.0 — production stack configuration
# Copy to `.env` and fill in.
# Never commit your real .env.
# ============================================================================

# --- Image versions ----------------------------------------------------------
OCS_BACKEND_REF=3.0.0-rc1
OCS_FRONTEND_REF=3.0.0-rc1

# --- Public access -----------------------------------------------------------
# Public base URL of the stack (single hostname for the UI and the API).
PUBLIC_URL=https://ocs.example.com

# URL sub-paths. Must be non-root, slash-wrapped (/x/), and differ from each other.
FRONTEND_BASE_PATH=/front/ # UI -> https://<host>/front/
API_BASE_PATH=/api/ # API + agents -> https://<host>/api/

# URL the browser uses to reach the API.
# Leave empty for a single-hostname deployment (derived from PUBLIC_URL + API_BASE_PATH).
# Set explicitly only when the API lives on a different host (split-origin).
BACKEND_API_ROUTE=

# Published ports of the edge proxy.
# Non-standard ports are supported; the HTTP->HTTPS redirect uses HTTPS_PORT.
# With a non-standard HTTPS port, include it in PUBLIC_URL (e.g. https://ocs.example.com:8443).
HTTP_PORT=80
HTTPS_PORT=443

# --- Django security ---------------------------------------------------------
DEBUG=False

# Required.
# Generate once and keep it stable:
# openssl rand -hex 50
SECRET_KEY=

# Comma-separated allowed hostnames.
# The loopback hosts (localhost, 127.0.0.1) are always allowed for the in-container healthcheck, so you only list your public domain(s) here.
ALLOWED_HOSTS=ocs.example.com
# Comma-separated trusted origins (scheme + host) for CSRF / OIDC / CAS.
CSRF_TRUSTED_ORIGINS=https://ocs.example.com

# SSO only (CAS/OIDC): URL the backend redirects the browser to after a login callback, with the auth token appended in the URL fragment.
# Leave empty to derive it as PUBLIC_URL + FRONTEND_BASE_PATH + /ocsreports (so it follows the UI base path).
# Set explicitly only for a split-origin setup.
# Unused for local username/password login.
FRONTEND_REDIRECT=

LOG_LEVEL=INFO

# --- Database (PostgreSQL) ---------------------------------------------------
# Only PostgreSQL is supported; the engine is pinned in the image.
DB_NAME=ocsdb
DB_USER=ocsuser
DB_PASSWORD=change-me
DB_HOST=db
DB_PORT=5432

# --- First admin -------------------------------------------------------------
# Bootstrap password for the 'admin' superuser.
# Applied only while the account still has its default password; once changed (here or via the UI) it is never touched again.
# Rotate later via the UI or `manage.py changepassword`.
OCS_ADMIN_PASSWORD=change-me

# --- Automation scheduler ----------------------------------------------------
# Seconds between scheduler runs.
AUTOMATION_INTERVAL=300

# --- Gunicorn (optional) -----------------------------------------------------
# GUNICORN_WORKERS=5
# GUNICORN_TIMEOUT=120

3. Provide TLS certificates

Put your certificate and key in a certs/ folder next to compose.yaml:

mkdir -p certs
# certs/fullchain.pem — certificate + chain
# certs/privkey.pem — private key

For a local test, generate a self-signed pair:

openssl req -x509 -newkey rsa:2048 -nodes \
-keyout certs/privkey.pem -out certs/fullchain.pem -days 365 -subj "/CN=localhost"

4. Launch

docker compose pull
docker compose up -d
docker compose logs -f ocs-init # watch migrations run, then exit 0

What happens: db becomes healthy → ocs-init applies migrations and exits → backend and automation start → frontend serves the UI and proxies the API.

5. First login

Open https://<your-host>/front/ocsreports and sign in as admin with the OCS_ADMIN_PASSWORD you set. Then point your OCS agents at the API — see Admin, auth & agents.

Update to a new OCS version

Bump the version tags in .env, then pull the new images and restart:

# .env
OCS_BACKEND_REF=3.0.0-rc2
OCS_FRONTEND_REF=3.0.0-rc2
docker compose pull
docker compose up -d

ocs-init runs the new migrations automatically (idempotent), and the database volume and SECRET_KEY are preserved — so your data, sessions and API tokens survive the update. The target version must be published to GHCR; see Upgrading for details and rollback.

Useful commands

docker compose ps # status + health
docker compose logs -f backend automation # app + scheduler logs
docker compose pull && docker compose up -d # update to newer images
docker compose exec backend python manage.py changepassword admin # reset admin password
docker compose down # stop (keeps data volumes)

Next