Overview
Quark ERP / Quan ERP deploys as a base stack (Postgres, Redis, backend, frontend) plus versioned plugins. On a fresh server, use the install script to prepare Docker, Nginx, and start compose. You develop plugins under plugins/, build them into available-plugins/, then activate them through the ERP UI into installed-plugins/. The base images and @quan-erp/* packages must stay on the same version line.
- installation/ — ./install.sh (or installation.sh) for host bootstrap + compose start/stop
- docker-compose.yaml — Postgres, Redis, backend, frontend
- plugins/ — source; quan-erp CLI builds into available-plugins/
- ERP UI install — copies / activates into installed-plugins/
- Never SQL-seed the module table against UAT or production
1. Host bootstrap (./install.sh)
Server deployments ship an installation helper next to the production docker-compose.yaml (folder installation/). Run it as a normal user — not with sudo / as root. The script installs Docker Engine, the Compose plugin, and Nginx, then can start or stop the stack.
- Commands: install | start | stop (see ./install.sh --help)
- Do not run as root — the script uses sudo only where needed (packages, systemctl, docker group)
- install — detects Ubuntu/Debian or RHEL-family, installs Docker + Compose plugin + Nginx, enables services, adds your user to the docker group
- start — docker compose up -d for the stack next to installation/
- stop — docker compose stop for the same compose file
- Supported OS: Ubuntu, Debian, RHEL, CentOS, Fedora, Rocky, AlmaLinux
- After install, you may need a new shell (or re-login) so the docker group membership applies without sudo
1cd installation
2chmod +x ./install.sh # or ./installation.sh
3
4# One-time host setup (Docker + Compose + Nginx)
5./install.sh install
6
7# Start compose services
8./install.sh start
9
10# Stop compose services
11./install.sh stop2. Base stack (Docker Compose)
For local template work, start under base/ with compose directly. On a server prepared with ./install.sh, prefer ./install.sh start so host helpers run before up -d. The example stack is four services on a shared bridge network: db (Postgres), redis, backend, and frontend. Backend talks to Postgres and Redis by compose service name.
- Inside containers, hosts are compose service names (db, redis) — never localhost for cross-service traffic
- Typical volumes: ./data (db / redis / app data), ./available-plugins, ./backend/installed-plugins, ./backend/logs
- Match BASE_VERSION / image tags (base-backend, base-frontend) with plugin @quan-erp/* versions
- Template passwords and token secrets are for local/dev only — rotate before any shared environment
cd base
docker compose up -d
docker compose pscd installation
./install.sh start
./install.sh stop2b. Example docker-compose.yaml
Copy this local/dev template, then replace every change-me value, set VITE_BACKEND_API to a browser-reachable URL, and point image tags / BASE_VERSION at your release line. Backend connects directly to Postgres (db:5432).
- DATABASE_HOST / DB_HOST=db and DB_PORT=5432 — direct Postgres, no pooler in this template
- Optional: OPENAI_* / FIREBASE_CONFIG — set only when those integrations are required; never commit real private keys
1services:
2 db:
3 image: postgres:17
4 container_name: postgres
5 restart: always
6 environment:
7 POSTGRES_USER: postgres
8 POSTGRES_PASSWORD: change-me
9 POSTGRES_DB: quan-erp
10 ports:
11 - "5432:5432"
12 volumes:
13 - ./data/db-data:/var/lib/postgresql/data
14 networks:
15 - app-network
16
17 redis:
18 image: redis:7
19 container_name: quanerp-redis
20 restart: unless-stopped
21 ports:
22 - "6379:6379"
23 command: >
24 redis-server
25 --requirepass "change-me"
26 --appendonly yes
27 volumes:
28 - ./data/redis:/data
29 networks:
30 - app-network
31 healthcheck:
32 test: ["CMD", "redis-cli", "ping", "-a", "change-me"]
33 interval: 5s
34 timeout: 2s
35 retries: 5
36
37 backend:
38 build:
39 context: ./backend
40 dockerfile: Dockerfile
41 image: theparadance/quan-erp-base-backend:1.0.0
42 restart: unless-stopped
43 platform: linux/amd64
44 container_name: backend
45 ports:
46 - "8080:8080"
47 - "8081:8081"
48 environment:
49 NODE_ENV: production
50 MODE: dev
51 BASE_VERSION: 1.0.0
52 ORGANIZATION_ID: quan-erp
53
54 FRONTEND_URL: http://localhost
55 BACKEND_URL: http://localhost:8080
56 DEV_TOOL_URL: http://localhost:8081
57 PLUGIN_SERVER_URL: http://localhost:8082
58
59 ACCESS_TOKEN_SECRET: change-me
60 ACCESS_TOKEN_SECRET_EXPIRATION_TIME: 15m
61 REFRESH_TOKEN_SECRET: change-me
62 REFRESH_TOKEN_SECRET_EXPIRATION_TIME: 30d
63
64 DATABASE_HOST: db
65 DB_HOST: db
66 DB_USERNAME: postgres
67 DB_PASSWORD: change-me
68 DB_PORT: 5432
69 DB_SCHEMA: quan-erp
70 DB_SYNC: true
71
72 REDIS_HOST: redis
73 REDIS_PORT: 6379
74 REDIS_PASSWORD: change-me
75
76 CLUSTER_MODE: false
77 CLUSTER_MAX_COUNT: 2
78 ENV_ENCRYPTION_KEY: change-me-32-char-hex-or-secret
79 FIREBASE_CONFIG: ''
80
81 OPENAI_BASE_URL: https://api.deepseek.com
82 OPENAI_API_KEY: <sk-key>
83
84 CORS_ALLOWED_ORIGINS: http://localhost:3000,http://127.0.0.1:3000,http://localhost,https://localhost
85
86 AVAILABLE_PLUGINS_FOLDER: /app/available-plugins
87 INSTALLED_PLUGINS_FOLDER: /app/installed-plugins
88 APP_DATA_FOLDER: /app/data/data/
89 UPLOAD_FILE_TEMP_FOLDER: /app/data/temp
90 UPLOAD_FILE_FOLDER: /app/data/data/
91 PLUGINS_ENV_FILE: /app/data/data/plugin.env.json
92 RUNNER_FLOW_FILE: /app/data/data/workflows/
93
94 ROOT_ADMIN_USERNAME: admin
95 ROOT_ADMIN_PASSWORD: change-me
96 DEVELOPER_CONFIG_PASSWORD: change-me
97
98 MAX_USER_LIMIT: 5
99 MAX_ACTIVE_USER_LIMIT: 5
100 MAX_USER_SESSION_LIMIT_PER_USER: 1
101 MAX_ORGANIZATION_BRANCH: 1
102 MAX_ROLE_LIMIT: 5
103 MAX_SUBSCRIPTION_GRACE_PERIOD_DAYS: 14
104 SUBSCRIPTION_END_DATE: '2099-01-01'
105 volumes:
106 - ./backend/installed-plugins:/app/installed-plugins
107 - ./data:/app/data
108 - ./backend/logs:/app/logs
109 - ./available-plugins:/app/available-plugins
110 depends_on:
111 - db
112 - redis
113 networks:
114 - app-network
115 dns:
116 - 1.1.1.1
117 - 8.8.8.8
118
119 frontend:
120 build:
121 context: ./frontend
122 dockerfile: Dockerfile
123 image: theparadance/quan-erp-base-frontend:1.0.0
124 restart: unless-stopped
125 platform: linux/amd64
126 container_name: frontend
127 environment:
128 VITE_BACKEND_API: http://localhost:8080
129 VITE_MODE: dev
130 ports:
131 - "80:80"
132 volumes:
133 - ./data/frontend/web-env.json:/usr/share/nginx/html/web-env.json
134 networks:
135 - app-network
136
137networks:
138 app-network:
139 driver: bridge3. Services & ports
Default template wiring (adjust for your host / reverse proxy). Prefer not publishing db / redis to the public internet on real servers.
- db (postgres:17) — host 5432 → container 5432; backend sets DATABASE_HOST / DB_HOST=db and DB_PORT=5432; POSTGRES_DB / schema quan-erp; volume ./data/db-data
- redis — host 6379 → 6379; REDIS_HOST=redis with REDIS_PASSWORD; volume ./data/redis
- backend — :8080 (BACKEND_URL / API), :8081 (DEV_TOOL_URL); volumes for available-plugins, installed-plugins, ./data, logs
- frontend — :80; set VITE_BACKEND_API to a URL the browser can reach (LAN IP or public hostname, not only localhost inside Docker)
- Nginx on the host — installed by ./install.sh install for TLS / reverse proxy in front of frontend (and optionally API)
4. Deploy plugins
Plugins are not baked into the base images. Build them, stage them as available, then install through the product UI (or distribute a pack archive).
- Build output → available-plugins/<name>/<version>/ (or base/available-plugins/ in the Node template)
- After UI install → installed-plugins/<name>/ (or base/backend/installed-plugins/)
- Backend loads installed plugins whose module row is marked installed
- Frontend loads each installed plugin’s module.js and calls register(AppRegistry)
- Local/dev only: insert a module row so the plugin appears in the UI — never against UAT/prod
1# From repo root
2quan-erp build:prod sample-es
3# Continuous during development
4quan-erp watch sample-es
5
6# Distributable zip (frontend + backend + metadata)
7quan-erp pack:prod sample-es
8quan-erp pack:prod sample-es ./dist-archives5. Version alignment
Keep the platform and plugins on one version line to avoid runtime breaks.
- All @quan-erp/* deps in plugin package.json share the same version as the base images
- docker-compose image tags (base-backend, base-frontend) and BASE_VERSION stay in sync
- module.metadata.json pluginVersion / requiredBasedVersion must match what you built and install
- After upgrading base images, rebuild plugins against the matching @quan-erp/* packages
6. Environment configuration
Infrastructure settings come from compose environment blocks (and optional .env). Plugin-specific operator settings use @InjectEnv and should not require a redeploy when changed from the ERP UI. See Documentation → Backend → Environment variables for the full key list.
- DB_* / DATABASE_HOST — use db (port 5432) in the example stack; DB_SCHEMA matches POSTGRES_DB (e.g. quan-erp)
- REDIS_HOST / REDIS_PORT / REDIS_PASSWORD — must match the redis service requirepass
- ACCESS_TOKEN_* / REFRESH_TOKEN_* / ENV_ENCRYPTION_KEY — rotate away from template values
- CORS_ALLOWED_ORIGINS — comma-separated browser origins (include http://LAN-IP and https://hostname as needed)
- FRONTEND_URL / BACKEND_URL / DEV_TOOL_URL / VITE_BACKEND_API — URLs browsers and peers can actually reach
- AVAILABLE_PLUGINS_FOLDER / INSTALLED_PLUGINS_FOLDER / APP_DATA_FOLDER / PLUGINS_ENV_FILE — paths inside the backend container, backed by host volumes
- BASE_VERSION + image tags — keep aligned with plugin @quan-erp/* versions
- DB_SYNC — fine for early local/dev; use migrations for staging/production schema changes
- MODE — template often uses dev; production deployments should use the intended production mode
7. Staging & production checklist
Treat the template compose file as a starting point, not a finished production config.
- Run ./install.sh install on a clean Linux host before first start
- Change all default passwords (Postgres, Redis, ROOT_ADMIN_*, DEVELOPER_CONFIG_PASSWORD) and token / encryption secrets
- Do not publish Postgres or Redis ports publicly; terminate TLS on Nginx (or another reverse proxy)
- Persist ./data, installed-plugins, available-plugins, and logs on durable volumes / backups
- Disable unsafe defaults (example secrets, open CORS, DB_SYNC=true, weak mem_limit on production workloads) before go-live
- Ship plugins via pack / available-plugins + UI install — do not SQL-seed modules on prod
- Run TypeORM migrations for schema changes; do not rely on synchronize in production
- Confirm FRONTEND_URL, BACKEND_URL, and VITE_BACKEND_API match the public hostnames (not a private LAN IP unless that is intentional)
- Align base image tags and plugin @quan-erp/* versions before upgrading
- Never commit real FIREBASE_CONFIG private keys, npm tokens, or production passwords into compose files checked into git
8. Day-2 operations
- Start / stop stack: cd installation && ./install.sh start|stop
- Update base: pull/build new base-backend / base-frontend tags → recreate containers
- Update a plugin: quan-erp build:prod|pack:prod → place under available-plugins → install / upgrade in UI
- Health: docker compose ps; check backend logs
- Data: back up Postgres volume and APP_DATA_FOLDER regularly
- Rollback: keep previous plugin version folders and image tags until the new release is verified
1cd installation # or cd base for the Node template
2docker compose logs -f backend
3docker compose restart backend frontend
4docker compose down
5./install.sh startTroubleshooting
- ./install.sh install fails — unsupported OS, missing sudo, or Docker repo not reachable; install Docker manually if needed
- docker: permission denied — log out/in after install so the docker group applies
- start fails — ensure POSTGRES_* / REDIS_* and image tags exist in docker-compose.yaml
- Backend cannot reach DB — DATABASE_HOST / DB_HOST must be db, DB_PORT=5432; not localhost
- Frontend cannot call API — VITE_BACKEND_API must be a browser-reachable host (LAN IP / public URL), not only an in-compose hostname
- Plugin missing in UI — not in available-plugins, wrong version folder, or module row missing (local only)
- Installed but UI blank — frontend dist / module.js not copied; rebuild and reinstall
- Auth / CORS failures — FRONTEND_URL and CORS_ALLOWED_ORIGINS must include the browser origin
- Version errors after upgrade — base image tag and plugin @quan-erp/* packages out of sync
Need help with deployment?
If you run into trouble deploying Quark ERP, you can get support from The Paradance. Help may be free or billed as an additional charge depending on the case — scope, urgency, and environment.
- Include BASE_VERSION / image tags, OS, compose logs, and steps to reproduce
- Simple guidance may be free; hands-on setup, production hardening, or custom ops work may be charged
- We confirm free vs paid before starting paid work