Docker Deployment
Docker is the recommended deployment method for most users. Codex provides pre-built Docker images that include the web frontend.
Quick Start with Docker Run
The simplest way to run Codex is with a single docker run command using SQLite:
docker run -d \
--name codex \
-p 8080:8080 \
-v /path/to/your/library:/library:ro \
-v codex-data:/app/data \
-e PUID=1000 \
-e PGID=1000 \
-e CODEX_AUTH_JWT_SECRET="$(openssl rand -base64 32)" \
ghcr.io/ashdevfr/codex:latest
Access Codex at http://localhost:8080. On first launch, you'll be guided through a setup wizard to create your admin account.
Run id in your terminal to find your UID and GID. Use these values for PUID and PGID to avoid permission issues with mounted volumes.
Replace /path/to/your/library with the path to your comics, manga, or ebooks folder.
Understanding the Volumes
| Volume | Container Path | Purpose |
|---|---|---|
| Your library | /library | Your comics, manga, and ebooks (read-only) |
| codex-data | /app/data | SQLite database, thumbnails, and uploads |
Mount your media library as read-only (:ro) to prevent accidental modifications. Codex only needs read access to your files.
Quick Start with Docker Compose
For a more maintainable setup, use Docker Compose. Create a docker-compose.yml file:
services:
codex:
image: ghcr.io/ashdevfr/codex:latest
container_name: codex
ports:
- "8080:8080"
volumes:
# Your media library (read-only)
- /path/to/your/library:/library:ro
# Codex data: SQLite database, thumbnails, uploads
- codex-data:/app/data
environment:
# User/Group ID for file permissions (run `id` to find yours)
PUID: 1000
PGID: 1000
# Generate with: openssl rand -base64 32
CODEX_AUTH_JWT_SECRET: "your-secure-secret-here"
restart: unless-stopped
volumes:
codex-data:
Then start Codex:
# Start Codex
docker compose up -d
# View logs
docker compose logs -f codex
Access Codex at http://localhost:8080. On first launch, you'll be guided through a setup wizard to create your admin account.
Run id in your terminal to find your UID and GID. Use these values for PUID and PGID to avoid permission issues with mounted volumes.
Multiple Libraries
To add multiple library paths, mount each one separately:
services:
codex:
image: ghcr.io/ashdevfr/codex:latest
container_name: codex
ports:
- "8080:8080"
volumes:
# Multiple libraries
- /mnt/comics:/library/comics:ro
- /mnt/manga:/library/manga:ro
- /mnt/ebooks:/library/ebooks:ro
# Codex data
- codex-data:/app/data
environment:
CODEX_AUTH_JWT_SECRET: "your-secure-secret-here"
restart: unless-stopped
volumes:
codex-data:
Then create libraries in the Codex UI pointing to /library/comics, /library/manga, etc.
Using a Bind Mount for Data
If you prefer using a local directory instead of a Docker volume for Codex data:
services:
codex:
image: ghcr.io/ashdevfr/codex:latest
container_name: codex
ports:
- "8080:8080"
volumes:
- /path/to/your/library:/library:ro
# Use a local directory for data
- ./codex-data:/app/data
environment:
CODEX_AUTH_JWT_SECRET: "your-secure-secret-here"
restart: unless-stopped
This makes it easier to backup the database and thumbnails.
PostgreSQL Setup (Optional)
For larger libraries or multi-user setups, you can use PostgreSQL:
services:
postgres:
image: postgres:16-alpine
container_name: codex-postgres
environment:
POSTGRES_USER: codex
POSTGRES_PASSWORD: codex
POSTGRES_DB: codex
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U codex"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
codex:
image: ghcr.io/ashdevfr/codex:latest
container_name: codex
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
volumes:
- /path/to/your/library:/library:ro
- codex-data:/app/data
environment:
CODEX_AUTH_JWT_SECRET: "your-secure-secret-here"
CODEX_DATABASE_DB_TYPE: postgres
CODEX_DATABASE_POSTGRES_HOST: postgres
CODEX_DATABASE_POSTGRES_PORT: 5432
CODEX_DATABASE_POSTGRES_USERNAME: codex
CODEX_DATABASE_POSTGRES_PASSWORD: codex
CODEX_DATABASE_POSTGRES_DATABASE_NAME: codex
restart: unless-stopped
volumes:
postgres-data:
codex-data:
Separate Worker Containers (Advanced)
For high-performance setups or when you need to scale workers independently from the web server, you can run dedicated worker containers. This requires PostgreSQL.
Separating workers from the web server allows you to:
- Scale background task processing independently
- Isolate resource-intensive tasks (scanning, thumbnail generation) from API requests
- Run more workers than web server instances
This example uses YAML anchors (x-codex-common) to share configuration between services:
# Shared configuration for all Codex services
x-codex-common: &codex-common
image: ghcr.io/ashdevfr/codex:latest
volumes:
- /path/to/your/library:/library:ro
- codex-data:/app/data
environment: &codex-env
CODEX_AUTH_JWT_SECRET: "your-secure-secret-here"
CODEX_DATABASE_DB_TYPE: postgres
CODEX_DATABASE_POSTGRES_HOST: postgres
CODEX_DATABASE_POSTGRES_PORT: 5432
CODEX_DATABASE_POSTGRES_USERNAME: codex
CODEX_DATABASE_POSTGRES_PASSWORD: codex
CODEX_DATABASE_POSTGRES_DATABASE_NAME: codex
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
services:
postgres:
image: postgres:16-alpine
container_name: codex-postgres
environment:
POSTGRES_USER: codex
POSTGRES_PASSWORD: codex
POSTGRES_DB: codex
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U codex"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Web server (API only, no workers)
codex:
<<: *codex-common
container_name: codex-web
ports:
- "8080:8080"
environment:
<<: *codex-env
# Disable workers in the web container
CODEX_DISABLE_WORKERS: "true"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 10s
timeout: 5s
retries: 5
# Dedicated worker container
codex-worker:
<<: *codex-common
container_name: codex-worker
# Run the worker command instead of serve
command: ["codex", "worker"]
environment:
<<: *codex-env
# Number of parallel task workers
CODEX_TASK_WORKER_COUNT: "4"
# Skip migrations (web container handles them)
CODEX_SKIP_MIGRATIONS: "true"
depends_on:
postgres:
condition: service_healthy
codex:
condition: service_healthy
volumes:
postgres-data:
codex-data:
Key Configuration Options
| Variable | Description | Default |
|---|---|---|
CODEX_DISABLE_WORKERS | Disable background workers in web container | false |
CODEX_SKIP_MIGRATIONS | Skip database migrations on startup | false |
CODEX_TASK_WORKER_COUNT | Number of parallel task workers | 4 |
Scaling Workers
To run multiple worker instances:
docker compose up -d --scale codex-worker=3
Separate worker containers require PostgreSQL. SQLite does not support multiple processes accessing the database simultaneously.
Volume Considerations
| Volume | Purpose | Permissions |
|---|---|---|
/app/data | Database (SQLite), thumbnails, uploads | Read-write |
/library | Media files | Read-only (recommended) |
Health Checks
You can add health checks to your Docker Compose configuration:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 10s
timeout: 5s
retries: 5
User/Group Permissions (PUID/PGID)
Codex supports PUID and PGID environment variables to run with specific user/group permissions. This is essential when using bind mounts to ensure the container can read/write to your host directories.
| Variable | Description | Default |
|---|---|---|
PUID | User ID to run as | 1000 |
PGID | Group ID to run as | 1000 |
To find your user's UID and GID:
id
# Output: uid=1000(youruser) gid=1000(youruser) ...
Example with Custom PUID/PGID
services:
codex:
image: ghcr.io/ashdevfr/codex:latest
environment:
PUID: 1000
PGID: 1000
CODEX_AUTH_JWT_SECRET: "your-secret-here"
volumes:
- ./codex-data:/app/data
- ./codex-config:/app/config
- /mnt/media:/library:ro
Synology DSM often uses different UIDs. Check your user's ID with id via SSH, or use the admin user's UID (usually 1026 or similar).
Environment Variables
Common environment variables for Docker:
| Variable | Description | Example |
|---|---|---|
PUID | User ID for file permissions | 1000 |
PGID | Group ID for file permissions | 1000 |
CODEX_AUTH_JWT_SECRET | JWT signing secret | your-secret-key |
CODEX_DATABASE_DB_TYPE | Database type | postgres or sqlite |
CODEX_DATABASE_POSTGRES_HOST | PostgreSQL host | postgres |
CODEX_DATABASE_POSTGRES_PASSWORD | PostgreSQL password | secret |
CODEX_LOGGING_LEVEL | Log level | info, debug |
See Configuration for all options.
Updating
# If using docker run
docker pull ghcr.io/ashdevfr/codex:latest
docker stop codex
docker rm codex
# Then run the docker run command again
# If using Docker Compose
docker compose pull
docker compose up -d
# Check logs for migration status
docker compose logs codex
Troubleshooting
Container Won't Start
# Check logs
docker compose logs codex
# Or for docker run
docker logs codex
Database Connection Issues
# Test PostgreSQL connection
docker compose exec postgres psql -U codex -d codex -c "SELECT 1"
# Check network
docker compose exec codex ping postgres
Permission Issues
If you see Permission denied (os error 13) errors:
-
Check your host user's UID/GID:
id
# uid=1000(user) gid=1000(user) ... -
Set PUID/PGID in your docker-compose.yml:
environment:
PUID: 1000 # Your UID from step 1
PGID: 1000 # Your GID from step 1 -
Verify permissions inside the container:
docker compose exec codex ls -la /app/data
docker compose exec codex ls -la /app/config
docker compose exec codex ls -la /library
The container will automatically adjust the internal user to match your PUID/PGID, ensuring it can write to mounted volumes.