QuantDingerQuantDingerv5.0.1 Docs 文档中心
中 / EN GitHub

QuantDinger Cloud Deployment Guide

This guide describes the current production-style Docker deployment for QuantDinger on a cloud server. It covers the recommended GHCR image deployment, optional source deployment, Nginx, HTTPS, upgrades, and common troubleshooting.

For first-time Docker pull and Postgres startup issues, also see Installation Troubleshooting.

Use one public domain with a host-level Nginx reverse proxy:

  • Public web URL: https://app.example.com
  • Optional mobile H5 URL: https://m.example.com
  • Host Nginx listens on 80/443
  • Docker frontend binds to 127.0.0.1:8888
  • Docker mobile binds to 127.0.0.1:8889
  • Docker backend binds to 127.0.0.1:5000
  • Docker postgres and redis bind to localhost only

Only expose 80 and 443 to the public internet. Keep 5000, 5432, and 6379 private.

1. Prepare the Server

Recommended baseline:

  • Ubuntu 22.04 / 24.04 or Debian 12
  • 2 vCPU / 4 GB RAM minimum; 4 vCPU / 8 GB RAM is better for AI-heavy use
  • 30 GB+ disk space
  • Security group or firewall allows 22, 80, and 443
  • A domain such as app.example.com

Create DNS records:

app.example.com -> your server public IP
m.example.com   -> your server public IP  # optional mobile H5 domain

Verify DNS:

ping app.example.com

2. Install Docker

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
docker --version
docker compose version

Use Compose v2 commands: docker compose ....

3. Choose a Deployment Mode

Use this mode for normal cloud deployment. It pulls backend, web frontend, and mobile H5 images from GHCR. No local Python or Node build is required.

mkdir -p ~/quantdinger
cd ~/quantdinger
curl -O https://raw.githubusercontent.com/brokermr810/QuantDinger/main/docker-compose.ghcr.yml
curl -o backend.env https://raw.githubusercontent.com/brokermr810/QuantDinger/main/backend_api_python/env.example

Edit backend.env before first start:

ADMIN_USER=your_admin_user
ADMIN_PASSWORD=your_strong_password
FRONTEND_URL=https://app.example.com,https://m.example.com
ALLOW_LOCAL_DESKTOP_BROKERS=false

The GHCR backend entrypoint can generate SECRET_KEY on first start and write it back to backend.env. You may also set SECRET_KEY manually to a long random string.

Create an optional project-root .env for Compose orchestration:

FRONTEND_PORT=127.0.0.1:8888
MOBILE_PORT=127.0.0.1:8889
BACKEND_PORT=127.0.0.1:5000
DB_PORT=127.0.0.1:5432
REDIS_PORT=127.0.0.1:6379

# Pin a release instead of floating latest, for example:
# IMAGE_TAG=5.0.1

# Use a Docker Hub mirror for postgres/redis when needed:
# IMAGE_PREFIX=docker.m.daocloud.io/library/

Start:

docker compose -f docker-compose.ghcr.yml pull
docker compose -f docker-compose.ghcr.yml up -d
docker compose -f docker-compose.ghcr.yml ps

Optional: full repository deployment

Use this mode only when you need to build the backend from local source.

git clone https://github.com/brokermr810/QuantDinger.git
cd QuantDinger
cp backend_api_python/env.example backend_api_python/.env
./scripts/generate-secret-key.sh

Edit backend_api_python/.env:

ADMIN_USER=your_admin_user
ADMIN_PASSWORD=your_strong_password
FRONTEND_URL=https://app.example.com,https://m.example.com
ALLOW_LOCAL_DESKTOP_BROKERS=false

Optionally create project-root .env with the same port settings shown above.

Start:

docker compose pull
docker compose up -d --build
docker compose ps

4. Understand the Two Env Files

Keep these files separate:

File Used by Purpose
backend.env docker-compose.ghcr.yml backend container Runtime app config: admin account, SECRET_KEY, LLM keys, OAuth, broker keys
backend_api_python/.env full repository backend container Same runtime app config when building from source
project-root .env Docker Compose Ports, image tags, image paths, Postgres image/data options, image mirrors

Do not put secrets such as exchange API keys into the project-root .env unless Compose explicitly needs them.

5. Configure Nginx

Install Nginx:

sudo apt update
sudo apt install -y nginx

Create /etc/nginx/sites-available/quantdinger.conf:

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

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:8888;
        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;
    }
}

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

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:8889;
        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;
    }
}

If you do not need a separate mobile domain, omit the second server block. Users can still access the mobile H5 service through the bound port or a path/domain you configure yourself.

Enable:

sudo ln -s /etc/nginx/sites-available/quantdinger.conf /etc/nginx/sites-enabled/quantdinger.conf
sudo nginx -t
sudo systemctl reload nginx

If using UFW:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

6. Enable HTTPS

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com -d m.example.com
sudo certbot renew --dry-run

If you only configured app.example.com, request only that domain.

Open:

https://app.example.com
https://m.example.com

7. Optional API Subdomain

The recommended setup keeps API traffic same-origin through the frontend container:

Browser -> https://app.example.com -> host Nginx -> frontend container -> /api -> backend:5000

If you need a separate api.example.com, expose only the host-local backend through Nginx:

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

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:5000;
        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;
    }
}

Also set FRONTEND_URL in the backend runtime env to include every public frontend origin.

8. Operations

For GHCR deployment:

docker compose -f docker-compose.ghcr.yml ps
docker compose -f docker-compose.ghcr.yml logs -f backend
docker compose -f docker-compose.ghcr.yml logs -f postgres
docker compose -f docker-compose.ghcr.yml restart backend

Update GHCR images:

docker compose -f docker-compose.ghcr.yml pull
docker compose -f docker-compose.ghcr.yml up -d

For full repository deployment:

git pull
docker compose pull
docker compose up -d --build

Back up Postgres before major upgrades:

docker exec quantdinger-db pg_dump -U quantdinger quantdinger > quantdinger_backup.sql

9. Postgres 18 and Existing Data

The current default Postgres image is postgres:18.3-alpine, with PGDATA=/var/lib/postgresql/18/docker.

If you already have a Postgres 16 data volume, do not start it with Postgres 18 directly. Either:

  • keep using a matching Postgres 16 image until you migrate;
  • export/import with pg_dump and pg_restore;
  • run a proper pg_upgrade migration.

For a disposable development database only, use the command that matches your deployment mode.

GHCR deployment:

docker compose -f docker-compose.ghcr.yml down -v
docker compose -f docker-compose.ghcr.yml up -d

Full repository deployment:

docker compose down -v
docker compose up -d

Do not use down -v on production data.

10. Troubleshooting

Image pull failures

If redis, postgres, or Docker Hub images fail to pull, set an image mirror in project-root .env:

IMAGE_PREFIX=docker.m.daocloud.io/library/

Then retry:

docker compose -f docker-compose.ghcr.yml pull

If GHCR images fail:

docker pull ghcr.io/brokermr810/quantdinger-backend:latest
docker pull ghcr.io/brokermr810/quantdinger-frontend:latest
docker pull ghcr.io/brokermr810/quantdinger-mobile:latest

Common causes include network blocks, private package visibility, or a pinned tag that does not exist.

Backend exits immediately

Check:

docker compose -f docker-compose.ghcr.yml logs --tail=100 backend

Common causes:

  • invalid backend env syntax;
  • missing or placeholder SECRET_KEY in full repository mode;
  • database not healthy;
  • wrong DATABASE_URL override.

Nginx 502 or blank page

Check local services first:

curl http://127.0.0.1:8888/health
curl http://127.0.0.1:8889/health
curl http://127.0.0.1:5000/api/health
sudo nginx -t

Then inspect containers:

docker compose -f docker-compose.ghcr.yml ps
docker compose -f docker-compose.ghcr.yml logs --tail=100 frontend
docker compose -f docker-compose.ghcr.yml logs --tail=100 backend

Exchange or LLM network requests need a proxy

For backend runtime outbound requests, set PROXY_URL in backend.env or backend_api_python/.env.

Inside Docker, do not use 127.0.0.1 for a host proxy unless the proxy is running inside the same container. Use a reachable host address, for example:

PROXY_URL=socks5h://host.docker.internal:10808

On Linux, you may need to expose your proxy on a private interface or configure Docker host gateway support.

Public ports

Do not expose these publicly:

  • 5000
  • 5432
  • 6379

Publicly expose only:

  • 80
  • 443