This is not a line-by-line explanation of the sample files. It is a deployment configuration guide focused on where each setting belongs, how priority works, and which decisions must be stable before production release.
Configuration Model#
DEEIX Chat separates configuration into three categories: startup configuration, runtime business settings, and frontend build configuration. Startup configuration determines whether the service can connect to database, cache, storage, and complete boot. Runtime business settings define models, routing, billing, RAG, MCP, and file-processing policy. Frontend build configuration determines which API origin the browser calls. Keeping these categories separate gives deployment, migration, secret rotation, and debugging a clear operational boundary.
| Layer | Scope | Stored in | Change path |
|---|---|---|---|
| Startup configuration | Ports, public URLs, database, cache, storage, secrets, GeoIP, tracing | config.yaml and environment variables | Edit and restart the service |
| Business configuration | Login policy, models, upstreams, routing, file processing, RAG, MCP, billing, announcements | Database system_settings | Change from the admin console |
| Frontend build configuration | Public API URL used by the browser | Build-time environment variable | Rebuild the frontend |
The effective priority is always environment variables > config.yaml > built-in defaults. If the container receives POSTGRES_DSN, REDIS_ADDR, or secrets through environment variables, those values override matching fields in config.yaml.
Deployment Shapes#
Choose the deployment shape before copying a template. The three templates map to different dependency models and should not be mixed casually. Lightweight deployment is for quick validation. Standard deployment connects to existing infrastructure. Full deployment runs the app, PostgreSQL, and Redis in one Compose stack.
| Deployment shape | Best for | Template | Compose file | Data and cache |
|---|---|---|---|---|
| Lightweight deployment | Local evaluation, demos, small single-node installs | config.sqlite.example.yaml | docker-compose.sqlite.yml | SQLite + memory cache |
| Standard deployment | Existing PostgreSQL, Redis, or platform infrastructure | config.example.yaml | docker-compose.yml | External PostgreSQL + Redis |
| Full deployment | One machine running app, database, and cache | config.full.example.yaml | docker-compose.full.yml | PostgreSQL + Redis inside Compose |
- Choose a deployment shape.
- Copy the matching template to
config.yaml. - Configure public URLs, security secrets, database, cache, and storage.
- After startup, configure models, routing, files, billing, and login policy from the admin console.
terminal
The backend reads config.yaml by default. Running from the repository root or Docker /app reads ./config.yaml; running from backend/ reads ../config.yaml. Use CONFIG_FILE for custom paths, and use the container path in Docker.
config.yaml#
config.yaml should contain only infrastructure settings that must be known before the service starts. It should answer where the service listens, what public URLs it uses, which database and cache it connects to, where files are written, and which security secrets protect the system. Models, upstreams, routes, pricing, subscriptions, RAG, MCP, and file-processing policy belong to the admin console, not static YAML.
Service URLs#
For public deployments, API URL, web URL, CORS, and reverse proxy settings must be reviewed together.
- Database DSNs target the container network.
public_api_base_urlis used by browsers, callbacks, and generated API links.public_web_base_urlis used by shares, redirects, and notification links.- Separated frontend/backend deployments must also align
NEXT_PUBLIC_API_BASE_URLat frontend build time.
config.yaml
| Field | Purpose | Production requirement |
|---|---|---|
app.env | Enables production validation | Use prod or production |
server.http_port | Go service listen port | Update Compose port mapping if changed |
server.cors_allow_origin | Frontend origins allowed to call the API | Use exact origins, not wildcard |
server.trusted_proxies | Trusted reverse proxy CIDRs | Include only real proxy sources |
server.public_api_base_url | Public API URL | Must be HTTPS in production |
server.public_web_base_url | Public web URL | Must be HTTPS in production |
Security Secrets#
Sample secrets are for local development only and must be replaced before public deployment. In production mode, the backend rejects empty values, built-in development defaults, and secrets that are too short. Environment variables override matching values in config.yaml.
| Secret | Minimum requirement | Notes |
|---|---|---|
security.jwt_secretJWT_SECRET | 16 characters | Signs login sessions and access tokens; rotate through a controlled secret-rotation process. |
security.data_encryption_keyDATA_ENCRYPTION_KEY | 32 characters | Derives the AES-GCM key for upstream API keys, SSO secrets, MCP tokens, sensitive settings, and TOTP secrets; keep it stable after release. |
Security secret generator
security.jwt_secret
JWT_SECRETsecurity.data_encryption_key
DATA_ENCRYPTION_KEYGenerated locally in your browser. Keys are not collected, stored, or sent anywhere.
ssrf_protection_enableddefaults tofalse; enable it for public deployments only after reviewing upstream, MCP, file-processing, and internal-network access requirements.- If secrets are injected through Kubernetes, Cloudflare, Docker Secret, or platform environment variables, the same length requirements still apply.
- Plan data migration or re-encryption before changing
data_encryption_key; existing ciphertext cannot be decrypted with a different value.
config.yaml
Data, Cache, and Storage#
Long-running multi-user deployments should use PostgreSQL + Redis. SQLite + memory cache is suitable for lightweight single-node installs and demos, but not for multi-instance, high-concurrency, or operationally strict production environments.
config.yaml
| Module | Recommended production setting | Notes |
|---|---|---|
| Database | PostgreSQL | Users, sessions, models, billing, audit logs, file indexes, and system settings |
| Cache | Redis | Required for multi-instance deployments; memory cache is only for one process |
| Storage | S3-compatible | Recommended for multi-instance or long-running production |
| SQLite | /app/data/deeix.db | Lightweight deployment only, with a persistent volume |
| Local storage | /app/storage | Stores uploaded and generated files; must be backed up |
GeoIP and Tracing#
GeoIP and OpenTelemetry do not block basic startup, but they affect audit context, tracing, and incident investigation.
- Prefer MMDB for stable audit enrichment instead of relying on external HTTP lookup.
- Enable tracing only after an OTLP Collector, sampling rate, and log-correlation plan exist.
- Avoid full tracing without a sampling strategy.
config.yaml
Docker Compose#
Compose defines runtime topology: image, ports, volumes, network, config mount, and deployment-level environment variables. It describes how containers run, not how product policy is managed. Models, routes, pricing, and file policy should remain in the admin console.
app Container Contract#
All Compose files are built around the app service.
- It binds to localhost by default; public traffic should normally go through a reverse proxy.
config.yamlis mounted read-only to avoid container-side drift.app_storageandapp_datamust be persistent.- Optional file-processing services join the same
deeix-chat-network.
docker-compose.yml
Full Deployment Overrides#
docker-compose.full.yml starts PostgreSQL and Redis, then injects environment variables that override connection settings. This is intentional: it ensures the app container connects to the internal Compose services.
docker-compose.full.yml
For production full deployments, replace at least the PostgreSQL password, Redis password, jwt_secret, and data_encryption_key.
Environment Overrides and Release Checks#
Environment variables are useful for container platforms, CI/CD, and secret managers. A practical rule: inject secrets through environment variables, keep stable structural settings in config.yaml, and keep runtime business settings in the admin console. Avoid maintaining the same value in multiple places; if an override is necessary, record where it comes from.
Common Override Variables#
| Area | Common variables | Config fields |
|---|---|---|
| Config file | CONFIG_FILE | Config file path |
| Service | APP_ENV, HTTP_PORT, PUBLIC_API_BASE_URL, PUBLIC_WEB_BASE_URL, CORS_ALLOW_ORIGIN | app.*, server.* |
| Security | JWT_SECRET, DATA_ENCRYPTION_KEY, SSRF_PROTECTION_ENABLED | security.* |
| Database | DATABASE_DRIVER, POSTGRES_DSN, SQLITE_PATH | database.* |
| Cache | CACHE_DRIVER, REDIS_ADDR, REDIS_PASSWORD, REDIS_DB | cache.*, database.redis.* |
| Storage | STORAGE_BACKEND, STORAGE_ROOT_DIR, STORAGE_S3_BUCKET, STORAGE_S3_SECRET_ACCESS_KEY | storage.* |
| Observability | OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_TRACES_SAMPLER_ARG | observability.tracing.* |
Production Checklist#
app.envisprodorproduction.jwt_secretanddata_encryption_keyare strong random values and stored in secret management.public_api_base_urlandpublic_web_base_urlare HTTPS public URLs.cors_allow_originuses exact frontend origins and no wildcard.trusted_proxiescontains only real reverse proxy CIDRs.- Production database uses PostgreSQL, and production cache uses Redis.
/app/dataand/app/storageare mounted to reliable persistent volumes with backups.- Multi-instance deployment uses S3-compatible storage instead of per-instance local storage.
- Default PostgreSQL and Redis passwords in Compose have been replaced.
- OpenTelemetry is enabled only after the collector, sampling rate, and log-correlation plan are clear.