This document describes how to run Codex with Docker and Docker Compose.
# Start services
docker compose up -d
# View logs
docker compose logs -f codex
# Stop services
docker compose down# Start development environment (backend + frontend + database)
docker compose --profile dev up -d
# View logs
docker compose logs -f codex-dev frontend-dev
# Stop
docker compose --profile dev downAccess the application:
- Frontend: http://localhost:5173 (use this URL - includes hot reload)
- Backend API: http://localhost:8080 (direct API access)
The frontend automatically proxies /api and /opds requests to the backend.
Docker Compose watch mode automatically syncs code changes:
# Start with watch mode
docker compose -f docker-compose.yml -f compose.watch.yml --profile dev up --watch
# Make changes to src/ files - they sync automatically!
# cargo-watch will detect changes and rebuild-
postgres - PostgreSQL 16 database
- Port: 5432
- User: codex
- Password: codex
- Database: codex
-
codex - Production application
- Port: 8080
- Built with release optimizations
- Auto-restarts on failure
-
codex-dev - Development application
- Port: 8080
- Hot reload with cargo-watch
- Debug logging enabled
- Source code mounted as volume
-
frontend-dev - Frontend development server (Vite)
- Port: 5173
- Hot module replacement (HMR)
- Proxies
/apiand/opdsto backend - Access the app at http://localhost:5173
- postgres-test - Test database
- Port: 5433 (different from main DB)
- User: codex_test
- Password: codex_test
- Database: codex_test
- Uses tmpfs (data cleared on restart)
docker compose --profile test up -d postgres-test
# Wait for it to be ready
docker compose --profile test ps# Set environment variables
export POSTGRES_HOST=localhost
export POSTGRES_PORT=5433
export POSTGRES_USER=codex_test
export POSTGRES_PASSWORD=codex_test
export POSTGRES_DB=codex_test
# Run ignored tests
cargo test --test postgres_integration_tests -- --ignored
cargo test db::postgres -- --ignoreddocker compose --profile test downThe application uses the config file at /app/config/config.yaml inside the container.
Mount your config:
docker run -v ./my-config.yaml:/app/config/config.yaml codexOr use environment variables in docker-compose.yml.
config/config.docker.yaml- PostgreSQL setup for Dockerconfig/config.sqlite.yaml- SQLite setup for local development
docker build -t codex:latest .docker build -f Dockerfile.dev -t codex:dev .docker buildx build --platform linux/amd64,linux/arm64 -t codex:latest .postgres_data- PostgreSQL database filescodex_data- Application data (if using file storage)
cargo_cache- Cargo registry cache (speeds up rebuilds)target_cache- Build artifacts cache
# List volumes
docker volume ls | grep codex
# Remove all volumes (CAUTION: deletes data!)
docker compose down -v
# Backup database
docker exec codex-postgres pg_dump -U codex codex > backup.sql
# Restore database
cat backup.sql | docker exec -i codex-postgres psql -U codex codexAll services use the codex-network bridge network.
Access between containers:
- Application → Database:
postgres:5432 - External → Application:
localhost:8080 - External → Database:
localhost:5432
# Check if postgres is healthy
docker compose ps
# View postgres logs
docker compose logs postgres
# Test connection manually
docker exec -it codex-postgres psql -U codex -d codex# View application logs
docker compose logs codex
# Check migrations
docker exec -it codex-app ls -la /app
# Connect to container
docker exec -it codex-app bash# Check cargo-watch logs
docker compose logs -f codex-dev
# Manually trigger rebuild
docker compose restart codex-dev
# Check file mounts
docker exec -it codex-dev ls -la /app/srcAvailable environment variables:
# Rust
RUST_LOG=debug # Logging level (error, warn, info, debug, trace)
RUST_BACKTRACE=1 # Enable backtraces
# PostgreSQL (for tests)
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=codex_test
POSTGRES_PASSWORD=codex_test
POSTGRES_DB=codex_test# Stop all services
docker compose --profile dev --profile test down
# Remove volumes (DELETES DATA!)
docker compose --profile dev --profile test down -v
# Remove images
docker rmi codex:latest codex:dev
# Full cleanup (removes everything)
docker system prune -a --volumesname: Test with PostgreSQL
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: codex_test
POSTGRES_PASSWORD: codex_test
POSTGRES_DB: codex_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run PostgreSQL tests
run: cargo test --test postgres_integration_tests -- --ignored
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
POSTGRES_USER: codex_test
POSTGRES_PASSWORD: codex_test
POSTGRES_DB: codex_test- Use bind mounts for source code (faster on macOS/Windows)
- Use volumes for caches (cargo_cache, target_cache)
- Use tmpfs for test database (faster tests)
- Multi-stage builds reduce final image size
- Cache dependencies layer speeds up rebuilds