11 - Docker Configuration

Overview

OpenAlgo provides Docker support for containerized deployment with 3-stage builds (Python builder, Frontend builder, Production), IST timezone configuration, and proper security isolation. The Docker setup uses Python 3.12, Gunicorn with Eventlet workers, and runs as a non-root user. It includes Railway/cloud deployment support with automatic .env generation.

Architecture Diagram

┌──────────────────────────────────────────────────────────────────────────────┐
│                        Docker Architecture (3-Stage Build)                    │
└──────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                        Stage 1: Python Builder                               │
│                        (python:3.12-bullseye)                                │
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │  1. Install build dependencies (curl, build-essential)                │  │
│  │  2. Copy pyproject.toml                                               │  │
│  │  3. Create virtual environment with uv                                │  │
│  │  4. Install dependencies: uv sync                                     │  │
│  │  5. Add gunicorn + eventlet>=0.40.3                                   │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                        Stage 2: Frontend Builder                             │
│                        (node:20-bullseye-slim)                               │
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │  1. Copy frontend/package*.json                                       │  │
│  │  2. npm install                                                       │  │
│  │  3. Copy frontend source                                              │  │
│  │  4. npm run build (React production build)                            │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│                        Stage 3: Production                                   │
│                        (python:3.12-slim-bullseye)                           │
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │  1. Set timezone to IST (Asia/Kolkata)                                │  │
│  │  2. Install runtime dependencies (curl, libopenblas0, libgomp1,       │  │
│  │     libgfortran5) for scipy/numba                                     │  │
│  │  3. Create non-root user (appuser)                                    │  │
│  │  4. Copy venv from python-builder                                     │  │
│  │  5. Copy application source                                           │  │
│  │  6. Copy frontend/dist from frontend-builder                          │  │
│  │  7. Create directories (log, db, strategies, keys, tmp, numba_cache)  │  │
│  │  8. Set permissions (keys: 700, others: 755)                          │  │
│  │  9. Run as appuser                                                    │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│                        Container Runtime (start.sh)                          │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────┐    │
│  │  1. Railway/Cloud Detection & .env Generation                       │    │
│  │     - Detects HOST_SERVER environment variable                      │    │
│  │     - Auto-generates .env with all required variables               │    │
│  │     - Supports 40+ configuration options                            │    │
│  │  2. Directory Setup                                                 │    │
│  │  3. Database Migrations (if /app/upgrade/migrate_all.py exists)     │    │
│  │  4. WebSocket Proxy (background, PID tracked)                       │    │
│  │  5. Signal Handling (SIGTERM, SIGINT cleanup)                       │    │
│  │  6. Gunicorn with Eventlet                                          │    │
│  │     - Single worker (-w 1) for WebSocket compatibility              │    │
│  │     - Timeout: 300s, Graceful timeout: 30s                          │    │
│  │     - Worker temp dir: /tmp/gunicorn_workers                        │    │
│  └────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  Exposed Ports:                                                             │
│  - 5000: Flask application (or PORT env var for Railway)                    │
│  - 8765: WebSocket proxy                                                    │
│  - 5555: ZeroMQ message bus (internal)                                      │
└─────────────────────────────────────────────────────────────────────────────┘

Dockerfile

Docker Compose

Named Volumes vs Bind Mounts

Approach
Pros
Cons

Named Volumes (recommended)

Better performance, managed by Docker

Data in Docker's volume directory

Bind Mounts (./db:/app/db)

Easy access to files

Permission issues possible

Directory Structure

Start Script

The start.sh script is a sophisticated 246-line entrypoint that handles:

  1. Railway/Cloud Environment Detection - Auto-generates .env from environment variables

  2. Directory Setup - Creates required directories with proper permissions

  3. Database Migrations - Runs upgrade/migrate_all.py if present

  4. WebSocket Proxy - Starts in background with PID tracking

  5. Signal Handling - Graceful shutdown on SIGTERM/SIGINT

  6. Gunicorn Startup - Eventlet worker with optimized settings

Key Differences from Simple Script

Feature
Old (6 lines)
Actual (246 lines)

Cloud Support

None

Full Railway/Render support

.env Generation

None

40+ variables auto-generated

Migrations

None

Auto-runs on startup

Signal Handling

None

Graceful shutdown

Timeout

120s

300s

Graceful Timeout

None

30s

Worker Temp Dir

Default

/tmp/gunicorn_workers

Build Commands

Docker Compose Commands

Environment Variables for Docker

Resource Configuration for Python Strategies

Running Python strategies with numerical libraries (NumPy, SciPy, Numba) in Docker requires careful resource configuration to prevent RLIMIT_NPROC exhaustion errors.

Thread Limiting Environment Variables

OpenBLAS, NumPy, and other numerical libraries spawn threads by default. In containers with limited process/thread limits, this causes crashes. The Dockerfile and docker-compose.yaml include these limits:

Variable
Purpose
Default

OPENBLAS_NUM_THREADS

OpenBLAS thread limit

2

OMP_NUM_THREADS

OpenMP thread limit

2

MKL_NUM_THREADS

Intel MKL thread limit

2

NUMEXPR_NUM_THREADS

NumExpr thread limit

2

NUMBA_NUM_THREADS

Numba JIT thread limit

2

Resource Scaling by Container RAM

Container RAM
Thread Limit
Strategy Memory
SHM Size
Max Strategies

2GB

1

256MB

256MB

5

4GB

2

512MB

512MB

5-8

8GB

2-4

1024MB

1GB

10+

16GB+

4

1024MB

2GB

20+

Configuration in docker-compose.yaml

Install Script Dynamic Calculation

The install-docker.sh script automatically calculates optimal values:

Reference: See GitHub Issue #822arrow-up-right for details on the RLIMIT_NPROC fix.

Security Considerations

Aspect
Implementation

Non-root user

Runs as appuser

Read-only .env

Mounted with :ro flag

Keys directory

700 permissions

No build tools

Slim production image

Minimal packages

Only runtime dependencies

Volume Persistence

Volume
Purpose
Required

/app/db

SQLite databases

Yes

/app/log

Application logs

Recommended

/app/strategies

User strategies

Optional

/app/.env

Configuration

Yes

Key Files Reference

File
Purpose

Dockerfile

Multi-stage build configuration

docker-compose.yml

Service orchestration

start.sh

Container entrypoint

.dockerignore

Build exclusions

Last updated