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.
Recommended Architecture¶
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
frontendbinds to127.0.0.1:8888 - Docker
mobilebinds to127.0.0.1:8889 - Docker
backendbinds to127.0.0.1:5000 - Docker
postgresandredisbind 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, and443 - 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¶
Recommended: GHCR prebuilt images¶
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_dumpandpg_restore; - run a proper
pg_upgrademigration.
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_KEYin full repository mode; - database not healthy;
- wrong
DATABASE_URLoverride.
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:
500054326379
Publicly expose only:
80443
