PACS AI Logo
|
Documentation: Configuration reference

Deploy

Configuration reference

Every environment variable that matters, grouped by concern, plus the compose overrides for common topologies.

api-pacs: core

VariableDefaultPurpose
API_NAME, API_URL_REST_PORTapi-pacs, 8000Do not change unless compose and nginx wiring change too
APP_URLnonePublic URL used in emails and links
API_BASE_URLhttp://api-pacs:8000/v1Internal base URL of the API
APP_TIMEZONEAmerica/TorontoIANA timezone for logs, ingestion windows and Postgres sessions
DOCKER_NETWORKpacs-netExternal network attached to inference containers
DOCKER_USERNAME, DOCKER_PASSWORDnoneRegistry credentials for private inference images (not the Docker socket, which compose mounts read-only)
POSTGRES_DB_HOST, POSTGRES_DB_PORT, POSTGRES_DB_DATABASE, POSTGRES_DB_USERNAME, POSTGRES_DB_PASSWORDpostgresql, 5432, db_pacs, pacs, dev passwordControl-plane database; replace the dev password before any non-dev install
REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_IAM_DBredis, 6379, dev password, 1IAM cache and rate-limit counters; the password must match redis/redis.conf and the cardio-agent REDIS_URL
ELASTICSEARCH_URL, KIBANA_BASE_URLhttp://elasticsearch:9200, http://kibana:5601Study index and log deep links
FIREBASE_PROJECT_ID, FIREBASE_CONFIG_FILE_PATH, FIREBASE_WEB_API_KEY, FIREBASE_SUPERUSER_KEYnoneFirebase Admin SDK, tenant-aware password sign-in, and the superuser key that gates first-owner bootstrap and privileged endpoints
ORTHANC_AET, ORTHANC_BASE_URL, ORTHANC_LOCAL_CACHE_EXPIRATION_IN_HOURSPACS_AI, http://orthanc:8042, 24Calling AE title, Orthanc REST URL, DICOM cache TTL
MAILGUN_API_KEY, MAILGUN_DOMAIN, MAILGUN_SENDER_EMAILnoneTransactional email
MAILCHIMP_API_KEY, MAILCHIMP_BASE_URL, MAILCHIMP_LIST_IDnoneOptional marketing list sync
DOCUSIGN_INTEGRATION_KEY, DOCUSIGN_USER_ID, DOCUSIGN_ACCOUNT_BASE_URI, DOCUSIGN_AUTH_SERVER, DOCUSIGN_PRIVATE_KEY, DOCUSIGN_ACCOUNT_IDnoneOptional DocuSign JWT auth for e-signature flows
OPENAPI_DOCS_PASSWORDnoneBasic-auth password for the /docs OpenAPI UI; empty disables the gate
ORCHESTRATOR_API_URLhttp://medical-agent:8585URL of the optional orchestrator service

api-pacs: ingestion, reconciliation and study-service handoff

VariableDefaultPurpose
INFERENCE_INGESTION_RUNNER_INTERVAL_MINUTES1Discovery (C-FIND) loop period
INFERENCE_INGESTION_RETRIEVAL_WORKER_INTERVAL_MINUTES1C-MOVE retrieval loop period
INFERENCE_INGESTION_RECONCILIATION_INTERVAL_MINUTES5Reconciliation loop period
INFERENCE_INGESTION_RECONCILIATION_PENDING_MINUTES, INFERENCE_INGESTION_RECONCILIATION_QUEUED_MINUTES, INFERENCE_INGESTION_RECONCILIATION_RUNNING_MINUTES2, 10, 65Per-state stale thresholds for processing executions
INFERENCE_INGESTION_RECONCILIATION_MODEL_RUNNING_MINUTESnoneJSON object of per-model running-stale overrides in minutes
INFERENCE_INGESTION_RECONCILIATION_FAILURE_THRESHOLD3Consecutive reconciliation failures before a run is flagged RECONCILIATION_FAILED
INFERENCE_INGESTION_RECONCILIATION_STALE_MINUTESunsetLegacy single threshold, used only as a fallback for the three per-state values
INFERENCE_INGESTION_DEFAULT_RECENT_WINDOW_MINUTES240C-FIND lookback window for jobs that do not set recent_window_minutes
STUDY_SERVICE_BASE_URLhttp://study-service:8600Execution plane URL
STUDY_SERVICE_INGEST_TOKEN, STUDY_SERVICE_OPERATOR_TOKEN, STUDY_SERVICE_CALLBACK_TOKENnoneThe three shared bearer tokens
STUDY_SERVICE_DISPATCH_CONCURRENCYnoneParallel dispatch fan-out; the only bound on automated ingestion, which is exempt from user quotas
INFERENCE_REQUIRE_PROCESSING_RUN_IDtrueStrict processing-run correlation; set false only during a controlled compatibility or rollback window

api-pacs: public registration, login protection and quotas

VariableDefaultPurpose
CLOUDFLARE_SECRET_KEY, CLOUDFLARE_TURNSTILE_BASE_URLnone, https://challenges.cloudflare.com/turnstile/v0Server-side Turnstile verification; required when public registration or adaptive login is enabled
REGISTRATION_RATE_LIMIT_WINDOW_SECONDS600Registration throttle window
REGISTRATION_RATE_LIMIT_TENANT_ATTEMPTS, REGISTRATION_RATE_LIMIT_EMAIL_ATTEMPTS, REGISTRATION_RATE_LIMIT_IP_ATTEMPTS100, 5, 10Attempts per tenant, per normalized email, per trusted client IP within the window; throttled requests return HTTP 429 REGISTRATION_RATE_LIMITED with Retry-After
REGISTRATION_TRUSTED_PROXY_CIDRS, LOGIN_TRUSTED_PROXY_CIDRSemptyDirect-proxy CIDRs allowed to supply X-Real-IP; use the exact pacs-net subnet (docker network inspect pacs-net) behind the bundled nginx. When empty, or when the peer is outside those networks, api-pacs ignores X-Real-IP and uses the socket peer address
POLICY_TERMS_VERSION, POLICY_TERMS_URL, POLICY_TERMS_EFFECTIVE_AT, POLICY_PRIVACY_VERSION, POLICY_PRIVACY_URL, POLICY_PRIVACY_EFFECTIVE_AT2026-08-15, https://pacsai.co/terms-of-service, 2026-08-15, 2026-08-15, https://pacsai.co/privacy-policy, 2026-08-15Versioned Terms of Service and Privacy Policy that registration must accept; publish these exact versions at the canonical HTTPS URLs before enabling public registration
POLICY_EXISTING_USER_GRACE_UNTILemptyOptional RFC3339 cutoff before existing users must accept current policies; empty means immediate enforcement
LOGIN_ABUSE_PROTECTION_ENABLEDtrueRedis-backed adaptive login protection; disabling is emergency-only, logs a security warning, and still keeps password verification server-side
LOGIN_FAILURE_WINDOW_SECONDS600Fixed login failure window
LOGIN_ACCOUNT_CHALLENGE_FAILURES3Account failures before Turnstile is required; the account scope never hard-locks
LOGIN_IP_CHALLENGE_FAILURES / LOGIN_IP_MAX_FAILURES5 / 30Client-IP challenge and hard-limit thresholds
LOGIN_TENANT_CHALLENGE_FAILURES / LOGIN_TENANT_MAX_FAILURES50 / 500Tenant challenge and hard-limit thresholds
LOGIN_TURNSTILE_ALLOWED_HOSTNAMESnoneExact frontend hostnames accepted for the login Turnstile action; production must not include localhost
INFERENCE_USER_QUOTA_WINDOW_SECONDS, INFERENCE_USER_QUOTA_ALLOWANCE86400, 50Per tenant and user inference units per window; one direct prediction or one manual reprocess consumes one unit
INFERENCE_USER_MAX_CONCURRENT_EXECUTIONS, INFERENCE_USER_RESERVATION_TTL_SECONDS2, 7200Active runs per user and the recovery TTL for a lost callback
INFERENCE_USER_QUOTA_TENANT_OVERRIDES_JSONemptyJSON keyed by tenant ID; fields windowSeconds, allowance, maxConcurrentExecutions, reservationTtlSeconds

Quota semantics: input or plan validation failures and dispatch failures are refunded; work accepted by a model stays charged even if the model later fails; manual-run concurrency is held until the run reaches a terminal state, with the reservation TTL acting only as recovery for lost callbacks. Rejections return HTTP 429 with INFERENCE_QUOTA_EXCEEDED or INFERENCE_CONCURRENCY_EXCEEDED; Redis unavailability fails closed with HTTP 503 INFERENCE_QUOTA_UNAVAILABLE. Clients read their allowance at GET /v1/inference/quota. Owners and admins are subject to the same quota unless their tenant has an override.

api-pacs: request and input safety

VariableDefaultPurpose
API_MAX_REQUEST_BODY_BYTES16777216Hard body limit for regular /v1 requests (HTTP 413 REQUEST_BODY_TOO_LARGE)
INFERENCE_PREDICT_MAX_REQUEST_BODY_BYTES1048576Stricter JSON limit for direct model predictions
DICOMWEB_MAX_REQUEST_BODY_BYTES, DICOMWEB_READ_TIMEOUT_SECONDS6442450944, 7200The only bundled large-upload exception (authenticated DICOMweb); keep consistent with nginx and do not raise the general API limits to accommodate DICOM uploads
INFERENCE_MAX_SERIES_UIDS, INFERENCE_MAX_METADATA_BYTES, INFERENCE_MAX_METADATA_DEPTH, INFERENCE_MAX_METADATA_ENTRIES256, 65536, 8, 256Prediction input ceilings, validated before DICOM retrieval; the model dicomUploadMin / dicomUploadMax are authoritative and may be stricter, and an invalid model range fails closed with HTTP 503
ORCHESTRATOR_MAX_REQUEST_BODY_BYTES8388608Body limit for orchestrator thread, chat and DICOM-selection JSON
ORCHESTRATOR_MAX_STUDIES_PER_REQUEST, ORCHESTRATOR_MAX_SERIES_UIDS_PER_STUDY20, 256Studies per orchestrator request and series UIDs per study
ORCHESTRATOR_MAX_PREVIEW_BASE64_BYTES2097152Maximum encoded preview or image string
ORCHESTRATOR_MAX_METADATA_BYTES, ORCHESTRATOR_MAX_METADATA_DEPTH, ORCHESTRATOR_MAX_METADATA_ENTRIES65536, 8, 256Orchestrator metadata size and structural limits
ORCHESTRATOR_MAX_MESSAGE_BYTES32768Maximum chat message length
HTTP_SERVER_READ_HEADER_TIMEOUT_SECONDS, HTTP_SERVER_READ_TIMEOUT_SECONDS, HTTP_SERVER_IDLE_TIMEOUT_SECONDS10, 30, 120Server timeouts; the write timeout stays disabled because the worklist SSE stream is long-lived

After changing nginx limits, rebuild the nginx service. After changing api-pacs limits or timeouts, recreate api-pacs so it receives the new environment.

nginx and Orthanc

FileVariables
nginx/.envSERVER_NAME; NGINX_FRONTEND_MAX_BODY_SIZE=1m, NGINX_API_MAX_BODY_SIZE=16m, NGINX_DICOMWEB_MAX_BODY_SIZE=6g (must be positive; zero or malformed values fail startup)
orthanc/.envORTHANC_DICOM_AET, ORTHANC_DICOM_PORT (no shipped default; 4242 in the development compose)
root .envAPP_TIMEZONE

study-service (cardio-agent/study-service/.env)

VariableDefaultPurpose
HOST, PORT, RELOAD, LOG_LEVEL, LOG_FORMAT0.0.0.0, 8600, false, info, textServer basics
DATABASE_URLnonePostgres URI for the cardio-agent database (5434); the repo default cardio:cardio is dev-only
REDIS_URLnoneRedis URI with password; integrated mode shares the root Redis on DB 2
ORTHANC_URL, ORTHANC_CHANGES_LIMIThttp://orthanc:8042, 100Orthanc REST and polling page size
ENABLE_ORTHANC_POLLING, ENABLE_GO_CALLBACKSfalse, trueExactly one must be true. The integrated topology uses callbacks (api-pacs hands off studies); standalone study-service uses polling
ORTHANC_POLL_START_MODE, ORTHANC_POLL_START_TIMESTAMP, ORTHANC_POLL_INTERVAL, ORTHANC_POLL_LOCK_TTLlatestPolling-mode settings (latest, backlog, since_timestamp)
GO_CALLBACK_BASE_URL, GO_CALLBACK_URL, GO_CALLBACK_TIMEOUT_SECONDS, GO_CALLBACK_MAX_RETRIES, GO_CALLBACK_TOKENnone, none, 5, 4, noneCallback target and auth (mirror of STUDY_SERVICE_CALLBACK_TOKEN)
STUDY_SERVICE_INGEST_TOKEN, STUDY_SERVICE_OPERATOR_TOKEN, STUDY_SERVICE_CALLBACK_TOKENnoneMirrors of the api-pacs values
ALLOW_UNAUTHENTICATED_INGEST, ALLOW_UNAUTHENTICATED_OPERATOR_ROUTESfalseDev-only auth bypasses
CELERY_WORKER_CONCURRENCY2Worker pool size
PROMETHEUS_MULTIPROC_DIR/tmp/prometheus-multiprocShared metrics directory so worker counters aggregate
CORS_ORIGINS, SHARED_DOCKER_NETWORK / CARDIO_AGENT_SHARED_NETWORKnone, pacs-netCORS and the external network

Compose overrides for common topologies

Compose merges a docker-compose.override.yml placed next to the root compose file.

  • No GPU host: set study-celery-worker to replicas: 0 with a disabled profile (and ollama if enabled), then point REDIS_URL in study-service at the GPU host Redis so a remote worker consumes the queue.
  • Managed PostgreSQL (RDS, Cloud SQL): remove postgresql/docker-compose.yml from the root include: list (and the inline postgres: block if cardio-agent moves too), then override POSTGRES_DB_* on api-pacs and DATABASE_URL on study-service and the worker. Run migrations from a host with the migrate binary.
  • Managed Redis (ElastiCache, Memorystore): remove redis/docker-compose.yml from include: and override REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_IAM_DB on api-pacs and REDIS_URL (DB 2) on study-service and the worker.
  • Air-gapped: on a connected host run docker compose pull and docker save -o pacs-ai-images.tar $(docker compose config --images); on the air-gapped host docker load -i pacs-ai-images.tar and set pull_policy: never for every service. Firebase still needs egress to *.googleapis.com; agree with the customer on a self-hosted IdP or a tunneled allow-list for that endpoint.
PACS AI Logo status Status Terms and Conditions Privacy Policy Privacy Impact Assessment Document (EFVP)

© 2026 HeartWise AI Lab, Montreal Heart Institute. All rights reserved.