Docs / OntiCards Docker Compose Deployment Guide

OntiCards Docker Compose Deployment Guide

This guide applies to the compose.yaml at the repository root. Production deployment is recommended on x86_64 (amd64) Linux servers; the project API image bundles the amd64 build of Microsoft ODBC and Oracle Instant Client, so ARM servers require additional adaptation.

1. Pre-deployment Preparation

1.1 Resource and Network Requirements

ItemMinimumRecommended for production
Operating SystemUbuntu 22.04 / 24.04 x86_64Ubuntu 24.04 x86_64
CPU2 cores4 cores or more
Memory4 GB8 GB or more
Disk30 GB50 GB or more (SSD)
DockerDocker Engine current stableDocker Engine current stable
ComposeDocker Compose v2.24.0+ pluginDocker Compose current stable plugin

Make sure the server can reach the required image registries, software sources, and model services. The first build of API and Web pulls container images and may also reach Debian APT, PyPI, Yarn/NPM, Microsoft software sources, and Oracle's download site. Configuring only a Docker image accelerator will not solve the network issues for these build dependencies.

Only Nginx exposes a port to the host, by default 9107. PostgreSQL, Weaviate, API, and Web communicate only on the internal Docker network and should not be exposed to the public network.

Published Docker ports may bypass UFW/firewalld rules. In production, restrict both cloud security groups and the host firewall, and open only the Nginx host ports actually in use. See Docker firewall limitations.

1.2 Current Service Topology

onticards-db + onticards-weaviate
              ↓
      onticards_api + onticards_web
              ↓
        onticards_nginx :9107
              ↓
          browser / outer reverse proxy

Compose automatically creates a bridge network named onticards; you do not need to run docker network create first. The startup order is "database and vector store containers first → API and Web containers → Nginx container".

This project has no Docker healthcheck, so it will not poll /healthz periodically or fill the application logs. service_started only guarantees the container startup order, not that the database or API has finished initialization. Use docker compose ps and the service logs to judge the actual state.

2. Install Docker and Docker Compose

2.1 Servers That Already Have Docker

Check the versions first:

docker --version
docker compose version

If the commands work and Compose is v2.24.0 or newer, skip the install. The current compose uses the env_file.required long syntax, which requires Docker Compose 2.24.0+. Do not uninstall docker, containerd, or modify /etc/docker/daemon.json to install on a server that is already running other Docker workloads; evaluate existing containers and the maintenance window first.

2.2 Ubuntu 22.04 / 24.04: Docker Official APT Repository

These steps apply to fresh Ubuntu servers. The commands come from the official Docker Engine Ubuntu install guide.

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

Verify the install:

sudo docker run --rm hello-world
docker compose version

The official Docker packages already include docker-compose-plugin. Use docker compose for day-to-day commands, not the legacy standalone docker-compose. See Compose install instructions.

To allow a non-root user to run Docker commands:

sudo usermod -aG docker "$USER"
newgrp docker

The docker group is equivalent to host root capability. Production servers should grant it under least-privilege. See Docker Linux post-installation.

2.3 RHEL / Rocky / AlmaLinux

Use the official Docker install flow for your distribution; do not reuse the Ubuntu APT commands. RHEL 8/9/10 can refer to Docker Engine on RHEL. After installing, also confirm:

docker --version
docker compose version
sudo systemctl enable --now docker

2.4 Windows Development Machine

For local development on Windows, install Docker Desktop for Windows with Linux containers and the WSL 2 backend. Docker Desktop already bundles Docker Engine, CLI, and Compose. After installing, verify in PowerShell:

docker version
docker compose version

Docker Desktop on Windows stores the mirror config in a different location from a Linux server; see the "Domestic / Enterprise Image Registry" section below.

3. Domestic or Enterprise Image Registry

3.1 First Identify the Type of Pull Failure

ResourceCurrent sourceCovered by Docker Hub accelerator?
PostgreSQLpostgres:15-alpineYes
Nginxnginx:1.27-alpineYes
API base imagepython:3.10-slim-bookwormYes
Web base imagenode:20.11-alpine3.19Yes
BuildKit Dockerfile frontenddocker/dockerfile:1Yes
Weaviatecr.weaviate.io/semitechnologies/weaviate:1.36.0No, must be mirrored or proxied separately
APT / pip / Yarn / Oracle build depsTheir own reposNo

Docker registry-mirrors is a Docker Hub accelerator; it does not automatically proxy cr.weaviate.io or other registries. Do not use public mirror sites of unknown origin — images may be stale, broken, or pose supply-chain risks.

3.2 Development / Test: Configure a Vendor-Specific Docker Hub Accelerator

Get an accelerator address bound to your own account from the cloud vendor console. For Alibaba Cloud ACR, the console generates a personal address; that doc positions it for personal development. For production, use image sync or a private registry.

On Linux Docker Engine, back up any current config first. If /etc/docker/daemon.json already exists, you must merge the following fields into the existing JSON — do not overwrite other config:

sudo install -d -m 0755 /etc/docker
sudo test ! -f /etc/docker/daemon.json || \
  sudo cp -a /etc/docker/daemon.json "/etc/docker/daemon.json.bak.$(date +%Y%m%d%H%M%S)"
sudoedit /etc/docker/daemon.json

Fill in the address from the cloud vendor console:

{
  "registry-mirrors": [
    "https://<vendor- or enterprise-assigned accelerator URL>"
  ]
}

Restart Docker in a maintenance window and verify. Restarting Docker may disrupt running containers:

sudo systemctl daemon-reload
sudo systemctl restart docker
docker info | sed -n '/Registry Mirrors:/,/Live Restore:/p'
docker pull nginx:1.27-alpine

The registry-mirrors format is documented in the dockerd reference and the Docker Hub mirror guide.

In Docker Desktop, do not modify the Linux /etc/docker/daemon.json. Open Settings → Docker Engine, merge the same JSON field in, and click Apply & Restart.

3.3 Production: Sync to a Controlled Domestic / Enterprise Private Registry

In a CI or build machine that can reach overseas mirrors, validate and sync the required images to your enterprise ACR / TCR / SWR / Harbor, then have production servers only access the controlled domestic or intranet registry. Pin versions or digests — do not use latest in production.

For Weaviate, the following commands must run on a machine that can reach cr.weaviate.io:

docker pull cr.weaviate.io/semitechnologies/weaviate:1.36.0
docker tag cr.weaviate.io/semitechnologies/weaviate:1.36.0 \
  registry.example.com/onticards/weaviate:1.36.0
docker login registry.example.com
docker push registry.example.com/onticards/weaviate:1.36.0

Also sync postgres:15-alpine, nginx:1.27-alpine, python:3.10-slim-bookworm, node:20.11-alpine3.19, and docker/dockerfile:1. Do not arbitrarily bump the Weaviate version — the current compose version matches the project Weaviate client.

To avoid modifying the repository's main compose file, create a per-environment compose.images.override.yaml in the project root:

services:
  onticards-db:
    image: registry.example.com/onticards/postgres:15-alpine

  onticards-weaviate:
    image: registry.example.com/onticards/weaviate:1.36.0

  onticards_nginx:
    image: registry.example.com/onticards/nginx:1.27-alpine

Start with the override file:

docker compose -f compose.yaml -f compose.images.override.yaml config -q
docker compose -f compose.yaml -f compose.images.override.yaml up -d --build

Production servers must log in to the private registry (or receive credentials from an enterprise system) before pulling:

docker login registry.example.com

Prefer robot accounts or credential helpers with pull-only permissions. Do not commit usernames, passwords, AccessKeys, or tokens to Git, .env.prod, the compose file, or the override file.

API and Web are built from source. If Docker Hub is fully unreachable, change the base images in the two Dockerfiles to your enterprise registry:

# OntiCards_Api/Dockerfile
FROM registry.example.com/onticards/python:3.10-slim-bookworm AS base

# OntiCards_Web/Dockerfile
FROM registry.example.com/onticards/node:20.11-alpine3.19 AS base

The first line # syntax=docker/dockerfile:1 of the API Dockerfile also depends on the BuildKit frontend from Docker Hub. In a fully offline environment, sync that image to the enterprise registry and replace the address, or pre-build the API/Web images on a connected build machine and push them to the enterprise registry.

3.4 No-Internet Environments

The most reliable approach is to build and export images on a connected build machine, then import them on the offline server:

# On the connected build machine: finish docker compose build and required docker pull
docker save -o onticards-images.tar \
  onticards-api:local onticards-web:local \
  postgres:15-alpine nginx:1.27-alpine \
  cr.weaviate.io/semitechnologies/weaviate:1.36.0

# On the offline server
docker load -i onticards-images.tar

# When API/Web images are already imported, prevent Compose from rebuilding from source
docker compose up -d --no-build --pull never

The offline server must also have the same source code, image tags, and runtime config as the build machine. For long-term production, prefer an enterprise private registry over manually transferring image tar files.

4. Get the Code and Configure Environment Variables

git clone https://github.com/stepll2026/OntiCards.git
cd OntiCards

The root .env.prod is a development/test default that starts out of the box. For production, create an untracked .env override file:

cp .env.prod .env
chmod 600 .env

When editing .env, at minimum replace the following sensitive settings:

CategorySettings to handle
PostgreSQLDB_PASSWORD; confirm DB_USERNAME, DB_DATABASE before the first start
App & SSOSECRET_KEY, SSO_SECRET_KEY
Data-source connection encryptionCONNECT_INFO_MASTER_KEY
Public-facing endpointsPUBLIC_BASE_URL, ALLOWED_ORIGINS, NGINX_SERVER_NAME, NGINX_HOST_PORT
Resources & logsGUNICORN_, WORKER_MEMORY_LIMIT_MB, LOG_

Docker Compose forces the API to use Docker service names:

DB_HOST=onticards-db
WEAVIATE_URL=http://onticards-weaviate:8080

Do not change these to host or external IPs. API and the database/vector store resolve via service names on the onticards network; external traffic should always go through Nginx.

After the first initialization, if ./volumes/postgresql/data already exists, modifying DB_USERNAME, DB_PASSWORD, or DB_DATABASE will not change the existing PostgreSQL account, password, or database name. Back up first and follow the PostgreSQL migration procedure.

Domain and Port Example

NGINX_SERVER_NAME=onticards.example.com
NGINX_HOST_PORT=9107
PUBLIC_BASE_URL=https://onticards.example.com
ALLOWED_ORIGINS=https://onticards.example.com

NGINX_SERVER_NAME is only the Nginx server_name slot. It does not issue HTTPS certificates automatically and is not an access whitelist. ALLOWED_ORIGINS is the browser CORS origin list, not IP access control.

5. Start and Verify

5.1 First Start

docker compose config -q
docker compose up -d --build
docker compose ps

docker compose config -q only validates the config; it does not start containers. The first up -d --build builds the API and Web images and starts five services.

When viewing logs, filter by service to avoid drowning the problem in unrelated noise:

docker compose logs --tail=200 onticards_api
docker compose logs --tail=200 onticards_web
docker compose logs --tail=200 onticards_nginx

Access entry point:

http://<server IP or domain>:9107/

You can check Nginx home reachability only; do not use /healthz:

curl -I http://127.0.0.1:9107/

5.2 Port 9107 In Use

Check the port:

sudo ss -lntp | grep ':9107'

Change the host port in the root .env, for example:

NGINX_HOST_PORT=19107

Then reapply the config:

docker compose up -d

The Nginx inside the container always listens on 9107; only the host mapping changes. Update PUBLIC_BASE_URL and ALLOWED_ORIGINS to the actual public address.

5.3 Domain and HTTPS

Recommend letting the cloud load balancer or an existing Nginx / Caddy on the host handle the certificate and 443, then reverse-proxy to the OntiCards host port:

server {
    listen 80;
    server_name onticards.example.com;

    location / {
        proxy_pass http://127.0.0.1:9107;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

After configuring the certificate, change PUBLIC_BASE_URL and ALLOWED_ORIGINS to https://onticards.example.com. Do not write HTTPS addresses into these two variables without a certificate.

6. Data Persistence, Backup, and Upgrade

The project uses host bind mounts instead of Docker named volumes. The directories to back up are:

volumes/postgresql/data/
volumes/weaviate/data/
volumes/governance/reports/
.env

When PostgreSQL is running, use a logical export to guarantee consistency:

mkdir -p backups
docker compose exec -T onticards-db \
  sh -c 'pg_dumpall -U "$POSTGRES_USER"' \
  > backups/postgresql-$(date +%F).sql

Weaviate file-level backups must run after stopping the service to avoid inconsistent data files:

docker compose stop onticards_api onticards_web onticards_nginx onticards-weaviate
tar -C volumes/weaviate -czf backups/weaviate-$(date +%F).tar.gz data
docker compose up -d

Governance reports can be backed up as files; if reports are being generated, run the backup during a low-traffic window or a write-stopped window for point-in-time consistency:

tar -C volumes/governance -czf backups/governance-reports-$(date +%F).tar.gz reports

If a physical file-level backup of the PostgreSQL data directory is required, stop the database and its dependent services first, or use a verified storage snapshot. Do not tar a running volumes/postgresql/data directly.

docker compose down stops and removes the containers and network but does not delete the host data directories above. Do not manually delete these directories without a backup.

Upgrade the application:

git pull --ff-only
# Manually diff .env.prod for new keys; do not overwrite production secrets in your .env.
docker compose config -q
docker compose up -d --build
docker compose ps

7. Common Operations and Troubleshooting

ScenarioCommand or action
Check statusdocker compose ps
View all logsdocker compose logs -f
View API logsdocker compose logs -f onticards_api
View Web logsdocker compose logs -f onticards_web
Restart a single servicedocker compose restart onticards_api
Stop the whole stackdocker compose down
Recreate containersdocker compose up -d --force-recreate
Validate configdocker compose config -q

Common issues:

  1. Image pull timeouts or i/o timeout: Configure a controlled accelerator or private registry per chapter 3. Weaviate and build-time repos require separate handling.
  2. API container keeps restarting: Run docker compose logs --tail=300 onticards_api and check database initialization, settings, external model services, or available memory. There is no healthcheck, so no /healthz polling log.
  3. Brief 502 right after start: Only the container startup order is guaranteed; the database/API may not accept connections during initial initialization. Wait for the logs to settle before retrying.
  4. Domain resolves but the browser request fails: Check PUBLIC_BASE_URL, ALLOWED_ORIGINS, the outer reverse proxy's Host / X-Forwarded-* headers, and cloud security group rules.
  5. Disk full: Use docker system df to inspect Docker usage first, then plan which images and build cache to keep. Do not run global cleanup commands without understanding the impact.

8. Pre-launch Checklist

  • [ ] Using Docker Engine and Compose v2; verified with docker compose version.
  • [ ] .env created with restricted permissions; production passwords and keys replaced.
  • [ ] DB_HOST=onticards-db and WEAVIATE_URL=http://onticards-weaviate:8080 left as Docker service names.
  • [ ] Only the Nginx host port is exposed; database, API, Web, and Weaviate are not exposed publicly.
  • [ ] A trusted private registry or a verified image-acceleration plan is in place for production.
  • [ ] Network policy for build deps (APT, PyPI, Yarn/NPM, Microsoft/Oracle, etc.) is verified.
  • [ ] volumes/ and .env are backed up, with a verified restore procedure.
  • [ ] Domain, HTTPS, cloud security groups, and log monitoring are configured.