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
| Item | Minimum | Recommended for production |
|---|---|---|
| Operating System | Ubuntu 22.04 / 24.04 x86_64 | Ubuntu 24.04 x86_64 |
| CPU | 2 cores | 4 cores or more |
| Memory | 4 GB | 8 GB or more |
| Disk | 30 GB | 50 GB or more (SSD) |
| Docker | Docker Engine current stable | Docker Engine current stable |
| Compose | Docker Compose v2.24.0+ plugin | Docker 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
| Resource | Current source | Covered by Docker Hub accelerator? |
|---|---|---|
| PostgreSQL | postgres:15-alpine | Yes |
| Nginx | nginx:1.27-alpine | Yes |
| API base image | python:3.10-slim-bookworm | Yes |
| Web base image | node:20.11-alpine3.19 | Yes |
| BuildKit Dockerfile frontend | docker/dockerfile:1 | Yes |
| Weaviate | cr.weaviate.io/semitechnologies/weaviate:1.36.0 | No, must be mirrored or proxied separately |
| APT / pip / Yarn / Oracle build deps | Their own repos | No |
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:
| Category | Settings to handle |
|---|---|
| PostgreSQL | DB_PASSWORD; confirm DB_USERNAME, DB_DATABASE before the first start |
| App & SSO | SECRET_KEY, SSO_SECRET_KEY |
| Data-source connection encryption | CONNECT_INFO_MASTER_KEY |
| Public-facing endpoints | PUBLIC_BASE_URL, ALLOWED_ORIGINS, NGINX_SERVER_NAME, NGINX_HOST_PORT |
| Resources & logs | GUNICORN_, 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
| Scenario | Command or action |
|---|---|
| Check status | docker compose ps |
| View all logs | docker compose logs -f |
| View API logs | docker compose logs -f onticards_api |
| View Web logs | docker compose logs -f onticards_web |
| Restart a single service | docker compose restart onticards_api |
| Stop the whole stack | docker compose down |
| Recreate containers | docker compose up -d --force-recreate |
| Validate config | docker compose config -q |
Common issues:
- 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. - API container keeps restarting: Run
docker compose logs --tail=300 onticards_apiand check database initialization, settings, external model services, or available memory. There is no healthcheck, so no/healthzpolling log. - 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.
- Domain resolves but the browser request fails: Check
PUBLIC_BASE_URL,ALLOWED_ORIGINS, the outer reverse proxy'sHost/X-Forwarded-*headers, and cloud security group rules. - Disk full: Use
docker system dfto 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. - [ ]
.envcreated with restricted permissions; production passwords and keys replaced. - [ ]
DB_HOST=onticards-dbandWEAVIATE_URL=http://onticards-weaviate:8080left 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.envare backed up, with a verified restore procedure. - [ ] Domain, HTTPS, cloud security groups, and log monitoring are configured.