Production Deployment
This guide covers deploying Codumentor in a production environment, including reverse proxy configuration, service management, data management, and operational best practices.
Docker Image
The deployment image is built from the same backend Dockerfile as the development container, but with a different target. Development uses target: dev through docker-compose.dev.yml; production uses the runtime target, which builds the UI and Linux executable in intermediate stages and copies only the executable into the final image.
docker build -f deploy/Dockerfile.backend --target runtime -t codumentor:latest .
Run it with your config, API keys, and persistent data mounted from outside the image:
docker run --rm \
--name codumentor \
-p 127.0.0.1:2638:2638 \
--env-file .env \
-v "$(pwd)/codumentor.yaml:/app/codumentor.yaml:ro" \
-v codumentor-data:/app/data \
-v codumentor-logs:/app/logs \
-v codumentor-repos:/app/repos \
codumentor:latest
For Docker deployments, prefer absolute in-container paths in codumentor.yaml:
repos_dir: /app/repos
vector_db:
path: /app/data/vector_db
agent:
storage_dir: /app/data/agent_storage
logging:
file: /app/logs/codumentor.log
The image runs as the non-root codumentor user, exposes port 2638, and defaults to:
/app/codumentor -c /app/codumentor.yaml api --host 0.0.0.0 --port 2638
Because that bind is reachable, the mounted /app/codumentor.yaml must configure an auth provider — the container refuses to start with auth.provider: "none" on 0.0.0.0. If the container network is your boundary and you accept an unauthenticated instance inside it, say so explicitly with auth.allow_unauthenticated_network_access: true. See Authentication.
For private Git repositories, mount SSH credentials or deploy keys as read-only files under /home/codumentor/.ssh.
Reverse Proxy
Codumentor should run behind a reverse proxy in production. Non-container deployments bind to 127.0.0.1:2638 by default. The Docker image binds to 0.0.0.0 inside the container, so keep the published port loopback-only unless another network boundary already protects it.
nginx Example
server {
listen 443 ssl;
server_name codumentor.example.com;
ssl_certificate /etc/ssl/certs/codumentor.pem;
ssl_certificate_key /etc/ssl/private/codumentor.key;
location / {
proxy_pass http://127.0.0.1:2638;
# Required for Server-Sent Events (SSE) streaming
proxy_set_header X-Accel-Buffering no;
proxy_set_header Connection keep-alive;
proxy_buffering off;
# Standard proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeout for long-running agent conversations
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
The SSE headers (X-Accel-Buffering: no, Connection: keep-alive, and proxy_buffering off) are essential. Without them, nginx buffers streaming responses and the web UI will not receive real-time updates.
Use a valid, CA-signed certificate — not a self-signed one. The browser's File System Access API (used by the Attach Local Folder feature, see Web UI) is only exposed in a secure context. A self-signed or otherwise invalid certificate does not qualify as a secure context, even if users click through the browser's certificate warning. In that situation the browser loads the page but the File System Access API is unavailable, so attaching a local folder silently fails. If you need a locally-issued certificate, distribute your internal CA to the client machines so the certificate chain validates, or use a public CA (e.g. Let's Encrypt).
Systemd Service
Create a systemd unit to manage the Codumentor API server as a background service.
Service Unit File
Create /etc/systemd/system/codumentor.service:
[Unit]
Description=Codumentor API Server
After=network.target
[Service]
Type=simple
User=codumentor
Group=codumentor
WorkingDirectory=/opt/codumentor
ExecStart=/opt/codumentor/.venv/bin/python -m codumentor.cli.main api
Restart=on-failure
RestartSec=5
# Environment variables for API keys (do not store in config files)
Environment="OPENAI_API_KEY=sk-..."
Environment="GOOGLE_API_KEY=..."
# Or load from an environment file:
# EnvironmentFile=/etc/codumentor/env
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/codumentor/data /opt/codumentor/logs
[Install]
WantedBy=multi-user.target
Managing the Service
# Reload after creating or editing the unit file
sudo systemctl daemon-reload
# Start the service
sudo systemctl start codumentor
# Enable start on boot
sudo systemctl enable codumentor
# Check status
sudo systemctl status codumentor
# View logs
sudo journalctl -u codumentor -f
Process supervisor
Every codumentor api start runs a small stdlib supervisor as the process
you launched. Bare codumentor does the same in the community edition (default
command api); enterprise defaults to tui, so bare enterprise does not start
the supervisor. The supervisor spawns a child that does the real work
(CODUMENTOR_SUPERVISED=1). It loads no configuration.
What the service manager sees. systemd, Docker, and a double-clicked
executable all track the supervisor's PID. Stopping that process stops the
child as well (Linux PDEATHSIG, a Windows job object, or a poll on macOS).
Do not point monitoring at the child's PID alone.
Admin restart vs crash recovery. From the Admin page, Restart server…
asks the child to exit with a restart request; the supervisor starts a new
child on the same port. That path needs no systemd unit change, no Docker
restart policy, and no shell on the host — the button works the same under
systemd, under Docker, and after a double-click. Ephemeral homes cannot
restart (their data would disappear with the dying child).
Crashes and ordinary exits still leave the supervisor: it does not
respawn a crashed child. Keep Restart=on-failure on the systemd unit (as in
the example above) and any Docker restart policy you already use so a dead
supervisor is brought back by the service manager. Admin restart is not a
failure exit that systemd or Docker must handle.
When to use --no-supervisor. Pass it when something outside Codumentor
must track a single PID that is the server (test harnesses, some launchers).
--reload (development) also skips the supervisor, because uvicorn already
owns a reloader process. Without a supervisor, the Admin restart button is
unavailable — restart with your service manager instead.
For what the confirmation dialog warns about and who may click Restart, see
Restarting the server from the Admin page.
Data Directories
Codumentor stores persistent data in the following directories. Ensure they exist and are writable by the service user before starting the application.
| Directory | Purpose | Default Path |
|---|---|---|
data/vector_db | ChromaDB vector database for indexed repository content | ./data/vector_db |
data/agent_storage | Agent conversation history and state | ./data/agent_storage |
logs/ | Application log files | ./logs/ |
mkdir -p /opt/codumentor/data/vector_db
mkdir -p /opt/codumentor/data/agent_storage
mkdir -p /opt/codumentor/logs
chown -R codumentor:codumentor /opt/codumentor/data /opt/codumentor/logs
Backup Strategy
What to Back Up
- SQLite databases -- Agent storage contains conversation history and session data. Back up all
.dbfiles underdata/agent_storage/. - Vector database directory -- The entire
data/vector_db/directory contains indexed repository content. This can be rebuilt by re-running ingestion, but backing it up avoids downtime. - Configuration files -- Back up
codumentor.yamland any extended config files.
Example Backup Script
#!/bin/bash
BACKUP_DIR="/backups/codumentor/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
# Stop the service briefly for consistent SQLite backups
sudo systemctl stop codumentor
cp -r /opt/codumentor/data/agent_storage "$BACKUP_DIR/"
cp -r /opt/codumentor/data/vector_db "$BACKUP_DIR/"
cp /opt/codumentor/codumentor.yaml "$BACKUP_DIR/"
sudo systemctl start codumentor
For zero-downtime backups, use SQLite's .backup command or sqlite3 CLI to create consistent copies without stopping the service.
Environment Variables for Production
API keys and secrets must be provided through environment variables, not stored in configuration files. Use the ${VAR_NAME} substitution syntax in codumentor.yaml:
models:
agent_api_key: "${OPENAI_API_KEY}"
embedding_api_key: "${GOOGLE_API_KEY}"
auth:
provider: ldap
ldap:
bind_password: "${LDAP_BIND_PASSWORD}"
Set the variables in the systemd EnvironmentFile, in your shell profile, or through your secrets manager. Never commit actual secret values to version control.
Logging in Production
JSON Logging
Enable structured JSON logging for integration with log aggregation systems (ELK, Splunk, Datadog):
logging:
level: INFO
file: ./logs/codumentor.log
json_logging: true
json_log_file: ./logs/codumentor.json.log
Each log entry is a single JSON line suitable for machine parsing:
{
"timestamp": "2026-02-16T10:30:00.123",
"level": "INFO",
"logger": "codumentor.api",
"message": "Request completed",
"context": {
"session_id": "abc123"
}
}
Log Rotation
Configure log rotation to prevent log files from consuming all disk space. Example logrotate configuration at /etc/logrotate.d/codumentor:
/opt/codumentor/logs/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
See Also
- Configuration -- Full configuration reference
- Monitoring -- Logging configuration and real-time log monitoring
- Security -- Security best practices for production
- Authentication -- Setting up authentication providers