ClouisleClouisle

Deployment architecture

Understand Clouisle service topology, request routing, dependencies, and persisted data

Clouisle runs 5 application services using 3 images. clouisle-backend provides api, worker, and beat; clouisle-sandbox-worker provides sandbox-worker; clouisle-frontend provides the Next.js frontend.

Service topology

ServiceImageRolePort
frontendclouisle-frontendNext.js standalone server3000
apiclouisle-backendFastAPI API8000
workerclouisle-backendCelery default, agent, knowledge, workflow queuesNone
sandbox-workerclouisle-sandbox-workerCelery sandbox queue and artifact uploadsNone
beatclouisle-backendCelery Beat scheduler, must run with a single replicaNone
dbPostgreSQL 17 + pg_searchBusiness, full-text retrieval, and audit data5432
redisRedis 7Queues, cache, sessions6379
qdrantQdrant 1.18.3Vector index6333

Celery queue layout

The clouisle-backend image starts api, worker, and beat through subcommands. Task-to-queue routing is declared statically in task_routes in backend/app/core/celery.py, and the consumed queue names are passed with -Q.

QueueConsumerTask modulesWork it carries
defaultworkerapp.tasks.usage, notification, audit_log, api_key, password_expiration, memoryUsage resets, notification delivery, audit log archiving, API key and password expiry checks
agentworkerapp.tasks.agentAgentRun execution (model calls and tool loop)
knowledgeworkerapp.tasks.knowledge_baseDocument parsing, chunking, embedding, and indexing
workflowworkerapp.tasks.workflowWorkflow runs
sandboxsandbox-workerapp.tasks.sandbox, tasks.cleanup_expired_sandbox_sessionsCode sandbox execution and expired sandbox session cleanup

The supplied Compose, Helm, and single-file manifests run the worker as python main.py worker -c 4 -Q default,agent,knowledge,workflow — one service consuming all four queues — and the sandbox worker as python main.py sandbox-worker -c <SANDBOX_WORKER_CONCURRENCY>, consuming only sandbox.

Periodic tasks dispatched by Beat: sweep-lost-agent-runs every 2 minutes on the default queue (reclaims AgentRuns still marked in-flight after a worker crash and is the only producer of INTERRUPTED), and cleanup-expired-sandbox-sessions every 15 minutes on the sandbox queue. Tasks on the agent queue set no retries: model calls and tool execution have side effects, so a retry would execute them twice.

Scaling rules:

  • docker compose up -d --scale worker=4 or kubectl scale deployment worker scales all four queues together. To scale one queue independently, run an extra worker container subscribed to a single queue, for example -Q agent to grow AgentRun execution only.
  • The sandbox queue is its own service; scaling sandbox-worker does not affect knowledge base or workflow processing.
  • beat always runs a single replica, see Service topology.

Request routing

An external reverse proxy or Ingress routes requests: /api/* to api:8000, everything else under / to frontend:3000. The frontend image does not include Nginx; deploy/nginx/default.conf is only an optional external example.

Keep the two hops separate:

  • Browser → API: the browser calls NEXT_PUBLIC_API_URL (frontend image build-time default /api/v1, i.e. a relative path), and the reverse proxy or Ingress forwards the /api prefix to api:8000. In Compose the frontend service additionally rewrites /api/* to BACKEND_INTERNAL_URL through Next.js rewrites.
  • Next.js server → API: BACKEND_INTERNAL_URL joined with the base path. The code default is http://localhost:8000; Compose injects http://api:8000; the single-file K8s manifest and the default Helm frontend workload carry no environment variables.

The backend mounts every router under API_V1_STR, default /api/v1: GET {API_V1_STR}/health and {API_V1_STR}/openapi.json move with it. If you change that value, update the proxy/Ingress path and the frontend build-time NEXT_PUBLIC_API_URL to the same /api/v1 prefix, or both browser and server-side calls will hit a path that does not exist.

Clouisle deployment architecture
Clouisle deployment architecture

Persisted data

Default Compose volumes: postgres_data, redis_data, qdrant_data, and uploads_data. Multiple API replicas using local upload storage require ReadWriteMany; with only ReadWriteOnce, keep a single API replica, while Worker and Sandbox Worker can still scale independently.

Internal addresses

Use service names inside containers, never localhost:

VariableCompose / K8s valuePurpose
POSTGRES_SERVERdb / postgresDatabase
REDIS_HOSTredisCache and Celery broker/backend
QDRANT_URLhttp://qdrant:6333Vector store
API_BASE_URLhttp://api:8000Internal API address for server-side use
API_INTERNAL_BASE_URLhttp://api:8000Where workers reach the upload gateway (UPLOAD_STORAGE_MODE=remote)
SANDBOX_ARTIFACT_UPLOAD_BASE_URLhttp://api:8000Sandbox worker artifact uploads

In Kubernetes, FRONTEND_URL is http://frontend:3000 (SSO redirect base) and clouisle-secret provides INTERNAL_API_TOKEN, which pods read through INTERNAL_API_TOKEN_FILE=/var/run/secrets/clouisle/internal-api-token. Worker and sandbox-worker mount no uploads volume; they go through this internal gateway instead.

How is this guide?

On this page