Docker Deployment
OpenTier’s entire server stack — database, schema migrations, intelligence engine, and API gateway — is orchestrated through a single docker-compose.yml in the server/ directory.
Prerequisites
Before deploying, ensure you have the following installed:
| Tool | Minimum Version | Purpose |
|---|---|---|
| Docker | 24.0+ | Container runtime |
| Docker Compose | 2.20+ (V2) | Multi-container orchestration |
| NVIDIA GPU drivers (optional) | 535+ | GPU acceleration for embeddings |
Quick Start
1. Configure Environment Variables
The server requires three .env files. Example templates are provided for each.
Database credentials (server/.env):
# Create from scratch — these are used by Docker Compose
cat > server/.env << 'EOF'
POSTGRES_USER=opentier
POSTGRES_PASSWORD=your-secure-password
POSTGRES_DB=opentier
# Optional: Intelligence service build arguments (for CPU-only deployments like AWS)
# INTELLIGENCE_BASE_IMAGE=debian:bookworm-slim
# INTELLIGENCE_UV_ARGS="--extra cpu"
EOFAPI service (server/api/.env):
# Copy and edit the example
cp server/api/.env.example server/api/.envKey variables to configure in server/api/.env:
| Variable | Required | Description |
|---|---|---|
GOOGLE_CLIENT_ID | For OAuth | Google OAuth app client ID |
GOOGLE_CLIENT_SECRET | For OAuth | Google OAuth app secret |
GITHUB_CLIENT_ID | For OAuth | GitHub OAuth app client ID |
GITHUB_CLIENT_SECRET | For OAuth | GitHub OAuth app secret |
SMTP_HOST | For email | SMTP server (e.g. smtp.gmail.com) |
SMTP_USERNAME | For email | SMTP login |
SMTP_PASSWORD | For email | SMTP password or app password |
CONTACT_EMAIL | No | Recipient for contact form emails (defaults to FROM_EMAIL) |
FRONTEND_URL | Yes | Frontend URL for email links (e.g. http://localhost:3000) |
Note:
DATABASE_URL,SERVER_HOST, andINTELLIGENCE_SERVICE_URLare automatically injected by Docker Compose. You do not need to set them manually.
Intelligence service (server/intelligence/.env):
# Copy and edit the example
cp server/intelligence/.env.example server/intelligence/.envKey variables to configure in server/intelligence/.env:
| Variable | Required | Description |
|---|---|---|
LLM_PROVIDER | Yes | openai or openai-compatible (for Ollama) |
LLM_API_KEY | Yes | API key for the LLM provider |
LLM_MODEL | Yes | Model name (e.g. gpt-4o, llama3) |
LLM_BASE_URL | Yes | API endpoint (e.g. https://api.openai.com/v1) |
Tip: For a free, local setup using Ollama , set
LLM_BASE_URL=http://host.docker.internal:11434/v1,LLM_API_KEY=ollama, andLLM_MODEL=llama3.
2. Deploy
From the project root:
cd server
docker compose upNote on GPU vs CPU: Be default, the intelligence service container will download and install PyTorch with massive NVIDIA CUDA dependencies suitable for GPU-enabled machines. If you are deploying on a CPU-only VM (such as AWS EC2) or simply want to save a few gigabytes of space, uncomment the
INTELLIGENCE_BASE_IMAGEandINTELLIGENCE_UV_ARGSvariables inserver/.envto force the Docker container to build a much smaller, CPU-only configuration.
That’s it. On first run, Docker will:
- Pull all required images
- Build the
intelligenceandapicontainers - Start PostgreSQL and wait for it to be ready
- Run all database migrations
- Start the intelligence engine
- Start the API gateway
The API will be available at http://localhost:4000.
3. Verify
Once you see the following in your terminal, the stack is fully operational:
opentier-db | database system is ready to accept connections
opentier-intelligence | gRPC server started on port 50051
opentier-api | Applied migration 20260111000001 create_users_table
opentier-api | ...
opentier-api | 🚀 API Gateway listening on http://0.0.0.0:4000You can also check service health:
# All containers running
docker compose ps
# API health check
curl http://localhost:4000/healthArchitecture
Startup Sequence
Docker Compose enforces a strict startup order using depends_on conditions:
| Step | Container | Image | What Happens |
|---|---|---|---|
| 1 | opentier-db | pgvector/pgvector:pg16 | PostgreSQL starts. On first run, creates the database and user. Healthcheck runs pg_isready every 5s. |
| 2 | opentier-intelligence | Built from server/intelligence/Dockerfile | Python gRPC service with PyTorch. Loads embedding model, connects to PostgreSQL, starts listening on port 50051. |
| 3 | opentier-api | Built from server/api/Dockerfile | Rust API gateway. Connects to PostgreSQL, runs sqlx migrate run, compiles cargo sqlx prepare and the API, and exposes HTTP API on port 4000. |
Network & Ports
All services communicate over a private Docker bridge network (opentier). Only the API gateway exposes a port to the host:
| Container | Internal Port | Host Port | Protocol |
|---|---|---|---|
opentier-db | 5432 | — (internal only) | PostgreSQL TCP |
opentier-intelligence | 50051 | — (internal only) | gRPC HTTP/2 |
opentier-api | 4000 | 4000 | HTTP REST + SSE |
Data Persistence
PostgreSQL data is stored in a named Docker volume (postgres_data). This volume persists across docker compose down and docker compose up cycles.
# Stop services but KEEP data
docker compose down
# Stop services and DELETE all data (fresh start)
docker compose down -vCommon Operations
Rebuild After Code Changes
# Rebuild and restart all services
docker compose up --build
# Rebuild only a specific service
docker compose up --build api
docker compose up --build intelligenceView Logs
# All services
docker compose logs -f
# Single service
docker compose logs -f api
docker compose logs -f intelligence
docker compose logs -f dbAdd a New Migration
Create the migration SQL files in server/db/migrations/ following the naming convention:
{YYYYMMDD}{HHMMSS}_{description}.up.sql
{YYYYMMDD}{HHMMSS}_{description}.down.sqlExample:
# Create migration files
touch server/db/migrations/20260305120000_add_user_preferences.up.sql
touch server/db/migrations/20260305120000_add_user_preferences.down.sqlThe next docker compose up will automatically apply the new migration.
Roll Back a Migration
# Roll back the last migration inside the API container
docker compose run --rm api sqlx migrate revert \
--source ../db/migrations \
--database-url "postgres://opentier:your-secure-password@db:5432/opentier?sslmode=disable"Reset the Database
# Nuclear option — drops volume, recreates everything from scratch
docker compose down -v
docker compose upConnect to the Database Directly
docker exec -it opentier-db psql -U opentier -d opentierTroubleshooting
pull access denied Error
If you see pull access denied for intelligence or pull access denied for api, it means Docker is trying to pull a pre-built image instead of building locally. Fix by explicitly building:
docker compose up --buildMigration Fails With “Already Exists”
This happens when the database has schema from a previous migration tool (e.g. sqlx). Reset the database:
docker compose down -v
docker compose upIntelligence Service Fails to Start
Check if the .env file exists and has valid LLM credentials:
cat server/intelligence/.env | grep LLM_The API will still boot even if intelligence is down — auth, user, and admin endpoints remain operational. Only chat functionality is degraded.
Port 4000 Already in Use
# Find what's using the port
# Windows
netstat -ano | findstr :4000
# Linux / macOS
lsof -i :4000
# Or change the port mapping in docker-compose.yml
ports:
- "8080:4000" # Map to host port 8080 instead