Skip to Content
DeploymentDocker Deployment

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:

ToolMinimum VersionPurpose
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" EOF

API service (server/api/.env):

# Copy and edit the example cp server/api/.env.example server/api/.env

Key variables to configure in server/api/.env:

VariableRequiredDescription
GOOGLE_CLIENT_IDFor OAuthGoogle OAuth app client ID
GOOGLE_CLIENT_SECRETFor OAuthGoogle OAuth app secret
GITHUB_CLIENT_IDFor OAuthGitHub OAuth app client ID
GITHUB_CLIENT_SECRETFor OAuthGitHub OAuth app secret
SMTP_HOSTFor emailSMTP server (e.g. smtp.gmail.com)
SMTP_USERNAMEFor emailSMTP login
SMTP_PASSWORDFor emailSMTP password or app password
CONTACT_EMAILNoRecipient for contact form emails (defaults to FROM_EMAIL)
FRONTEND_URLYesFrontend URL for email links (e.g. http://localhost:3000)

Note: DATABASE_URL, SERVER_HOST, and INTELLIGENCE_SERVICE_URL are 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/.env

Key variables to configure in server/intelligence/.env:

VariableRequiredDescription
LLM_PROVIDERYesopenai or openai-compatible (for Ollama)
LLM_API_KEYYesAPI key for the LLM provider
LLM_MODELYesModel name (e.g. gpt-4o, llama3)
LLM_BASE_URLYesAPI 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, and LLM_MODEL=llama3.

2. Deploy

From the project root:

cd server docker compose up

Note 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_IMAGE and INTELLIGENCE_UV_ARGS variables in server/.env to force the Docker container to build a much smaller, CPU-only configuration.

That’s it. On first run, Docker will:

  1. Pull all required images
  2. Build the intelligence and api containers
  3. Start PostgreSQL and wait for it to be ready
  4. Run all database migrations
  5. Start the intelligence engine
  6. 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:4000

You can also check service health:

# All containers running docker compose ps # API health check curl http://localhost:4000/health

Architecture

Startup Sequence

Docker Compose enforces a strict startup order using depends_on conditions:

StepContainerImageWhat Happens
1opentier-dbpgvector/pgvector:pg16PostgreSQL starts. On first run, creates the database and user. Healthcheck runs pg_isready every 5s.
2opentier-intelligenceBuilt from server/intelligence/DockerfilePython gRPC service with PyTorch. Loads embedding model, connects to PostgreSQL, starts listening on port 50051.
3opentier-apiBuilt from server/api/DockerfileRust 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:

ContainerInternal PortHost PortProtocol
opentier-db5432— (internal only)PostgreSQL TCP
opentier-intelligence50051— (internal only)gRPC HTTP/2
opentier-api40004000HTTP 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 -v

Common 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 intelligence

View Logs

# All services docker compose logs -f # Single service docker compose logs -f api docker compose logs -f intelligence docker compose logs -f db

Add 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.sql

Example:

# Create migration files touch server/db/migrations/20260305120000_add_user_preferences.up.sql touch server/db/migrations/20260305120000_add_user_preferences.down.sql

The 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 up

Connect to the Database Directly

docker exec -it opentier-db psql -U opentier -d opentier

Troubleshooting

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 --build

Migration 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 up

Intelligence 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
Last updated on