This document is the exact deployment sequence for running the API in production, including AWS S3 storage wiring.
- Linux server with Docker installed.
- Production PostgreSQL 16+ with
vectorextension enabled. - Production Redis 7+.
- Reachable Ollama endpoint with model
llama3.1pulled. - AWS account access to create an S3 bucket and IAM user.
git clone <repo-url>
cd agentic-rag-apiCreate a bucket (example name used below):
- Bucket:
agentic-rag-api - Region: same region as your deployment when possible
- Public access: keep blocked
Attach this policy (replace bucket name if different):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::agentic-rag-api/BACKEND/*"
}
]
}Create an access key for this IAM user and keep:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
aws s3api put-object --bucket agentic-rag-api --key BACKEND/healthcheck.txt --body /etc/hosts
aws s3api delete-object --bucket agentic-rag-api --key BACKEND/healthcheck.txtIf both commands succeed, S3 permissions are correct.
Create .env.production in repository root:
# Database
DATABASE_URL_ASYNC=postgresql+asyncpg://<db_user>:<db_pass>@<db_host>:5432/<db_name>
DATABASE_URL_SYNC=postgresql://<db_user>:<db_pass>@<db_host>:5432/<db_name>
DB_POOL_SIZE=10
DB_MAX_OVERFLOW=20
# Redis
REDIS_URL=redis://<redis_host>:6379/0
# Runtime controls
LOGIN_RATE_LIMIT_ATTEMPTS=10
LOGIN_RATE_LIMIT_WINDOW_SECONDS=60
CHAT_RATE_LIMIT_REQUESTS=30
CHAT_RATE_LIMIT_WINDOW_SECONDS=60
CHAT_IDEMPOTENCY_TTL_SECONDS=300
TOKEN_SESSION_CACHE_TTL_SECONDS=60
VECTOR_SEARCH_CACHE_TTL_SECONDS=300
CHAT_HISTORY_CACHE_TTL_SECONDS=60
DOCUMENT_METADATA_CACHE_TTL_SECONDS=60
READINESS_REQUIRE_REDIS=true
DEFAULT_LIST_LIMIT=50
MAX_LIST_LIMIT=200
MAX_UPLOAD_BYTES=26214400
# Security
SECRET_KEY=<strong-random-secret>
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60
# Embedding
EMBEDDING_MODEL=all-MiniLM-L6-v2
CHUNK_SIZE=1000
CHUNK_OVERLAP=200
# Encoding
ENCODING=utf-8
# Storage: enable S3
DOCUMENT_STORAGE=CLOUD_STORAGE
AWS_ACCESS_KEY_ID=<aws-access-key-id>
AWS_SECRET_ACCESS_KEY=<aws-secret-access-key>
AWS_REGION=<aws-region>
S3_BUCKET_NAME=agentic-rag-api
# Optional logging override
LOG_LEVEL=INFO
# Optional (when Ollama is remote):
# OLLAMA_HOST=http://<ollama-host>:11434Notes:
LOCAL_UPLOAD_DIRis not used whenDOCUMENT_STORAGE=CLOUD_STORAGE.- Object key format will be
BACKEND/{company_id}/{stem}_{DD-MM-YYYY_HH-MM-SS}.pdf.
docker build -t agentic-rag-api:v2026.06.27 .Do not deploy with floating latest tags in production. Compose is pinned directly to explicit image tags:
agentic-rag-api:v2026.06.27pgvector/pgvector:pg16redis:7-alpine
When releasing a new version, update the app image tag in docker-compose.yml.
Run migrations once per deployment:
docker run --rm --env-file .env.production agentic-rag-api:latest \
uv run alembic upgrade headdocker run -d \
--name agentic-rag-api \
--restart unless-stopped \
-p 8000:8000 \
--env-file .env.production \
agentic-rag-api:latestdocker run -d \
--name agentic-rag-worker \
--restart unless-stopped \
--env-file .env.production \
agentic-rag-api:latest \
uv run celery -A app.worker.celery_app worker --pool=prefork --loglevel=infoIf your environment does not support prefork, use --pool=solo.
curl -fsS http://<server-host>:8000/healthz
curl -fsS http://<server-host>:8000/readyz
docker compose psExpected:
/healthzreturns HTTP 200./readyzreturns HTTP 200 only when DB (and Redis when required) are reachable.docker compose psshould showhealthyfor app, worker, db, and redis.
- Upload a PDF through
POST /v1/documents/upload. - Check API logs for S3 initialization/upload success messages.
- Confirm object exists in bucket under
BACKEND/<company_id>/....
Useful commands:
docker logs --tail 200 agentic-rag-api
docker logs --tail 200 agentic-rag-worker
aws s3 ls s3://agentic-rag-api/BACKEND/ --recursive | tail -20On each release:
- Pull latest code.
- Rebuild image.
- Run migrations.
- Restart API and worker containers.
Commands:
git pull
docker build -t agentic-rag-api:latest .
docker run --rm --env-file .env.production agentic-rag-api:latest uv run alembic upgrade head
docker rm -f agentic-rag-api agentic-rag-worker
docker run -d --name agentic-rag-api --restart unless-stopped -p 8000:8000 --env-file .env.production agentic-rag-api:latest
docker run -d --name agentic-rag-worker --restart unless-stopped --env-file .env.production agentic-rag-api:latest uv run celery -A app.worker.celery_app worker --pool=prefork --loglevel=infoIf a release fails:
- Re-run old image tag containers.
- Keep DB restore plan ready before applying irreversible migrations.
Example rollback:
docker rm -f agentic-rag-api agentic-rag-worker
docker run -d --name agentic-rag-api --restart unless-stopped -p 8000:8000 --env-file .env.production agentic-rag-api:<previous-tag>
docker run -d --name agentic-rag-worker --restart unless-stopped --env-file .env.production agentic-rag-api:<previous-tag> uv run celery -A app.worker.celery_app worker --pool=prefork --loglevel=info- Strong
SECRET_KEYset. - DB and Redis endpoints are private/restricted.
- S3 bucket is private with least-privilege IAM policy.
- API and worker containers are both running.
alembic upgrade headexecuted successfully./healthzand/readyzreturn 200.- Test document upload creates object in S3.