Full Edition Deployment
The Full edition is aimed at multi-user, long-running instances that need independent backend services. The official Compose starts the app, PostgreSQL, Redis, Qdrant, the built-in Python sandbox, and the sandbox runtime image keep-alive service.
| Service | Responsibility | Exposed to the internet |
|---|---|---|
app | Serves both the web UI and /api | Yes, or reachable only through a reverse proxy |
postgres | Relational data: users, conversations, workspaces, settings, usage | No |
redis | Caching, rate limiting, cross-process events, and stopping streaming output | No |
qdrant | RAG vector indexes | No |
sandbox | Creates restricted Python session containers | No |
sandbox-image-keepalive | Keeps the sandbox runtime images warm to avoid cold pulls | No |
The Full edition itself is not a multi-machine cluster orchestration solution, but it provides clear boundaries for higher concurrency, independent data services, complete backups, and future growth.
Server and Network Preparation
Start with 4 vCPUs, 8 GB of memory, and an 80 GB SSD; heavy concurrent conversations, document parsing, vector indexing, or Python execution will need more memory, CPU, and disk. Use 64-bit Ubuntu 22.04/24.04 LTS or Debian 12 — both x86_64 and ARM64 work.
Before you start, confirm:
- Docker Engine and the Docker Compose Plugin are installed and working.
- The server can reach the image registry, model providers, and the mail, OAuth, search, embedding, or object storage services you plan to use.
- The firewall exposes only HTTP/HTTPS to users; do not add public port mappings or security-group rules for PostgreSQL, Redis, Qdrant, or the sandbox.
- The data disk has enough room for the database, vectors, uploads, artifacts, and backups.
DATA_DIRmust be writable by Docker. - If you enable the built-in sandbox, you accept the risk of that sidecar mounting the host Docker socket. It must be a host you fully control.
Read-only preflight example:
uname -m
docker version
docker compose version
df -h
Create the Deployment Configuration
git clone --depth=1 https://github.com/hjxwz123/Aivory.git /opt/aivory
cd /opt/aivory/deploy
cp .env.example .env
Edit .env and set at least three mutually distinct random values plus the persistent directory:
POSTGRES_PASSWORD=替换为独立的强随机数据库密码
REDIS_PASSWORD=替换为独立的强随机Redis密码
JWT_SECRET=替换为独立的强随机会话签名密钥
DATA_DIR=/opt/aivory/data
IMAGE_TAG=latest
Generate random values:
openssl rand -hex 32
Do not reuse the database password, the Redis password, and JWT_SECRET. In production, JWT_SECRET should be at least 32 characters and stay stable; changing it invalidates existing sessions. For a stable instance, pin IMAGE_TAG to a fully released version number, for example 3.0.0, and take a backup before upgrading.
The Full edition uses Qdrant on the internal network by default. With QDRANT_API_KEY left empty, the official Compose uses an internal shared default and the service publishes no port; in production, you should still set a strong random value distinct from your other keys. Change QDRANT_URL and its matching API key only when you deliberately connect to a controlled external Qdrant.
Render the Configuration Before Startup
The actual deployment reads .env, not the template .env.example. Inspect Compose's final resolution first, and especially confirm that the app, the sidecars, and the sandbox runtime image all use the same version tag:
cd /opt/aivory/deploy
docker compose --env-file .env -f docker-compose.prod.yml config
docker compose --env-file .env -f docker-compose.prod.yml config --images
The official Full edition explicitly pins the PostgreSQL, Redis, Qdrant, and sandbox connections between the app and the built-in services. So do not try to change the default topology by putting DATABASE_URL, REDIS_URL, or SANDBOX_BASE_URL into .env; explicit Compose environment values take precedence. When you need custom external services, review the full rendered result and the network boundaries first.
Pull, Start, and Basic Verification
cd /opt/aivory/deploy
docker compose --env-file .env -f docker-compose.prod.yml pull
docker compose --env-file .env -f docker-compose.prod.yml up -d
docker compose --env-file .env -f docker-compose.prod.yml ps
postgres and redis pass their health checks first, then the app starts. The first pull of the built-in sandbox may take a while because the Python runtime image is large; do not repeatedly delete containers or images while it is downloading.
Check the recent logs of the app and the built-in services:
docker compose --env-file .env -f docker-compose.prod.yml logs --tail=200 app
docker compose --env-file .env -f docker-compose.prod.yml logs --tail=100 postgres redis qdrant sandbox
curl -fsS http://127.0.0.1/api/health
The health endpoint is expected to return {"ok":true}. Container status should be running, and after its initial pull the sandbox should show as healthy. The app maps host 80 to container 8787 by default; confirm the local health check passes before pointing a domain or reverse proxy at it.
First Administrator and First Model
Open the server's IP or domain. A new instance has no pre-provisioned users: the first account created through the initialization page automatically becomes the administrator. After logging in, set up a working chat in this order:
- In "Channels", add an enabled provider connection with the correct API format, Base URL, and API Key.
- In "Models", manually add or auto-import at least one enabled chat model.
- In "Settings → Model policy", select the default chat model.
- Start a new conversation and send a short message to confirm that real model responses, usage records, and error prompts all behave as expected.
Model provider API keys are stored in the persistent configuration of the admin console. Never put them in frontend build variables, browser local settings, or public logs. A fuller walkthrough of the admin configuration order is in Administrator First-Run Configuration.
Domain, HTTPS, and Access Boundaries
The web UI and /api are served by the same app. For a domain or HTTPS reverse proxy, set ALLOWED_ORIGINS=https://chat.example.com in .env, then let Caddy, Traefik, Nginx, or a cloud load balancer terminate HTTPS and forward requests to app.
If the proxy and Compose run on the same host, change the Compose port mapping to 127.0.0.1:8787:8787, then point the proxy at 127.0.0.1:8787. Never let the proxy and the app compete for host ports 80/443, and never expose PostgreSQL, Redis, Qdrant, or the sandbox through the reverse proxy. Concrete examples and the callback rules for GitHub OAuth are in Domains, HTTPS, and OAuth.
Data, Sandbox, and Backup Boundaries
| Persistence location | Contents | Handling principle |
|---|---|---|
pgdata named volume | Relational data and admin-console settings | Back up together with all other data |
redisdata named volume | Redis append-only logs and cache-related data | Keep it for full restore capability |
qdrantdata named volume | Vector indexes | Back up in sync with the database and files |
sandbox-archives named volume | Archives for local persistent sandbox workspaces | Back these up as well when you use local persistent workspaces |
DATA_DIR | Uploads, artifacts, local objects, and admin-console backup files | Protect with a host-level backup strategy |
The built-in sandbox publishes no port to the public internet, but it creates session containers through the Docker socket. Do not treat it as the only line of defense for untrusted multi-tenant code execution; keep restricting which users, models, and scenarios can use Python, and leave resource headroom on the host. sandbox-image-keepalive exists so the next execution is not forced into a cold pull after image cleanup — do not delete it as a useless container.
For upgrades, rollbacks, full backups, restore verification, and cross-architecture migration, use Upgrades, Backups, and Recovery.
First Checks When Things Break
- The web UI won't load: check
applogs, host 80/443 listeners, the firewall, and the reverse proxy's upstream. - The app doesn't start: verify
POSTGRES_PASSWORD,REDIS_PASSWORD, andJWT_SECRETare actually filled into.env, then check thepostgres,redis, andapplogs. - The knowledge base returns no retrieval results: check the embedding dimension, document index status, and Qdrant logs.
- Python is unavailable: check
sandboxstatus, the Docker socket, the sandbox image pull, and the admin tools settings.
See Common Issue Troubleshooting for details.