sha256:2d88b30096ebfcb04a51d7fc2284e7ee031ec48bd255f9706b4478cfa2ecba85
Last pushed
about 3 hours by harlenalvarez
Type
Compose
Manifest digest
sha256:2d88b30096ebfcb04a51d7fc2284e7ee031ec48bd255f9706b4478cfa2ecba85
name: huddle
services:
web-renderer:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-web-renderer:${HUDDLE_IMAGE_TAG:-dev}
init: true
user: pwuser
security_opt:
- seccomp=./web-renderer/seccomp_profile.json
- no-new-privileges:true
cap_drop: [ALL]
read_only: true
tmpfs:
- /tmp:size=256m,mode=1777
- /home/pwuser/.cache:size=64m,uid=1001,gid=1001
shm_size: 1g
mem_limit: 4g
memswap_limit: 4g
cpus: 4
pids_limit: 1024
networks: [renderer]
healthcheck:
test:
[
CMD,
node,
-e,
"const s=require('net').connect(3000,'127.0.0.1');s.setTimeout(3000);s.on('connect',()=>{s.end();process.exit(0)});s.on('error',()=>process.exit(1));s.on('timeout',()=>process.exit(1))",
]
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
postgres:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-postgres:${HUDDLE_IMAGE_TAG:-dev}
# Arguments replace the image CMD, so keep its shared_preload_libraries.
command:
- postgres
- -c
- shared_preload_libraries=pg_textsearch
- -c
- shared_buffers=${POSTGRES_SHARED_BUFFERS:-1GB}
- -c
- effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE:-3GB}
- -c
- work_mem=${POSTGRES_WORK_MEM:-16MB}
- -c
- maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-256MB}
# Parallel queries use /dev/shm; the 64 MB default fails under load.
shm_size: 1g
mem_limit: 4g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_ALLOWED_HOSTS: postgres
volumes:
- postgres_data:/var/lib/postgresql
networks:
- backend
healthcheck:
# Check the private listener without assuming a role from DATABASE_URL;
# the custom entrypoint may initialize any configured username.
test:
[
'CMD',
'python3',
'-c',
"import socket; connection = socket.create_connection(('127.0.0.1', 5432), 3); connection.close()",
]
interval: 5s
timeout: 5s
retries: 20
start_period: 15s
restart: unless-stopped
zitadel-db-init:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-postgres:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python3', '/usr/local/lib/huddle/zitadel_db_init.py']
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_ALLOWED_HOSTS: postgres
ZITADEL_DB_NAME: ${ZITADEL_DB_NAME:-zitadel}
ZITADEL_DB_USER: ${ZITADEL_DB_USER:-zitadel}
ZITADEL_DB_PASSWORD: ${ZITADEL_DB_PASSWORD:?ZITADEL_DB_PASSWORD is required}
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY:?ZITADEL_MASTERKEY is required}
ZITADEL_SESSION_COOKIE_SECRET: ${ZITADEL_SESSION_COOKIE_SECRET:?ZITADEL_SESSION_COOKIE_SECRET is required}
depends_on:
postgres:
condition: service_healthy
volumes:
- zitadel_bootstrap:/zitadel/bootstrap
networks:
- backend
restart: 'no'
zitadel-api:
image: ghcr.io/zitadel/zitadel:${ZITADEL_VERSION:-v4.19.3}
user: '0'
command:
['start-from-init', '--masterkey', '${ZITADEL_MASTERKEY:?ZITADEL_MASTERKEY is required}']
environment:
ZITADEL_PORT: '8080'
ZITADEL_TLS_ENABLED: 'false'
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_DOMAIN:-localhost}
ZITADEL_EXTERNALPORT: ${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}
ZITADEL_EXTERNALSECURE: ${ZITADEL_EXTERNALSECURE:-false}
ZITADEL_DATABASE_POSTGRES_DSN: postgresql://${ZITADEL_DB_USER:-zitadel}:${ZITADEL_DB_PASSWORD:?ZITADEL_DB_PASSWORD is required}@postgres:5432/${ZITADEL_DB_NAME:-zitadel}?sslmode=disable
ZITADEL_FIRSTINSTANCE_ORG_NAME: ${ZITADEL_ADMIN_ORG:-Huddle Admin}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ZITADEL_ADMIN_EMAIL:?ZITADEL_ADMIN_EMAIL is required}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS: ${ZITADEL_ADMIN_EMAIL:?ZITADEL_ADMIN_EMAIL is required}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_VERIFIED: 'true'
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_FIRSTNAME: Huddle
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_LASTNAME: Admin
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_ADMIN_PASSWORD:?ZITADEL_ADMIN_PASSWORD is required}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: 'false'
ZITADEL_FIRSTINSTANCE_PATPATH: /zitadel/bootstrap/admin.pat
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME: huddle-bootstrap
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Huddle bootstrap administrator
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE: ${ZITADEL_ADMIN_PAT_EXPIRATION:-2027-10-01T00:00:00Z}
ZITADEL_FIRSTINSTANCE_LOGINCLIENTPATPATH: /zitadel/bootstrap/login-client.pat
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_USERNAME: login-client
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_NAME: Huddle Login v2
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_PAT_EXPIRATIONDATE: ${ZITADEL_LOGIN_PAT_EXPIRATION:-2027-10-01T00:00:00Z}
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: 'true'
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI: ${ZITADEL_PUBLIC_SCHEME:-http}://${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}/ui/v2/login/
ZITADEL_OIDC_DEFAULTLOGINURLV2: ${ZITADEL_PUBLIC_SCHEME:-http}://${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}/ui/v2/login/login?authRequest=
ZITADEL_OIDC_DEFAULTLOGOUTURLV2: ${ZITADEL_PUBLIC_SCHEME:-http}://${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}/ui/v2/login/logout?post_logout_redirect=
ZITADEL_SAML_DEFAULTLOGINURLV2: ${ZITADEL_PUBLIC_SCHEME:-http}://${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}/ui/v2/login/login?samlRequest=
# Invite-only from first start, before the operator runs bootstrap.
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWREGISTER: 'false'
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWUSERNAMEPASSWORD: 'true'
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_FORCEMFA: 'false'
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_HIDEPASSWORDRESET: 'true'
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWEXTERNALIDP: 'false'
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWDOMAINDISCOVERY: 'false'
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_PASSWORDLESSTYPE: '0'
depends_on:
zitadel-db-init:
condition: service_completed_successfully
volumes:
- zitadel_bootstrap:/zitadel/bootstrap
networks:
- backend
healthcheck:
test: ['CMD', '/app/zitadel', 'ready']
interval: 10s
timeout: 30s
retries: 12
start_period: 30s
restart: unless-stopped
zitadel-login:
image: ghcr.io/zitadel/zitadel-login:${ZITADEL_VERSION:-v4.19.3}
user: '0'
environment:
ZITADEL_API_URL: http://zitadel-api:8080
NEXT_PUBLIC_BASE_PATH: /ui/v2/login
ZITADEL_SERVICE_USER_TOKEN_FILE: /zitadel/bootstrap/login-client.pat
ZITADEL_SESSION_COOKIE_SECRET: ${ZITADEL_SESSION_COOKIE_SECRET:?ZITADEL_SESSION_COOKIE_SECRET is required}
CUSTOM_REQUEST_HEADERS: Host:${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}},X-Forwarded-Proto:${ZITADEL_PUBLIC_SCHEME:-http}
depends_on:
zitadel-api:
condition: service_healthy
volumes:
- zitadel_bootstrap:/zitadel/bootstrap:ro
networks:
- backend
healthcheck:
test: ['CMD', 'node', '/app/healthcheck.mjs', 'http://localhost:3000/ui/v2/login/healthy']
interval: 10s
timeout: 30s
retries: 12
start_period: 30s
restart: unless-stopped
zitadel:
image: caddy:2-alpine
environment:
ZITADEL_SITE_ADDRESS: ${ZITADEL_SITE_ADDRESS:-http://:8080}
configs:
- source: zitadel_caddyfile
target: /etc/caddy/Caddyfile
depends_on:
zitadel-login:
condition: service_healthy
zitadel-api:
condition: service_healthy
networks:
- frontend
- backend
healthcheck:
test:
['CMD', 'wget', '--quiet', '--output-document=/dev/null', 'http://127.0.0.1:2019/config/']
interval: 10s
timeout: 5s
retries: 12
restart: unless-stopped
redis:
image: redis:8-alpine
# Redis is a TTL cache (page, search and token caches), so evict the least recently
# used keys at the cap instead of failing writes. The container limit sits above
# maxmemory for the AOF rewrite fork and client buffers.
command:
- redis-server
- --appendonly
- 'yes'
- --maxmemory
- ${REDIS_MAXMEMORY:-1gb}
- --maxmemory-policy
- allkeys-lru
mem_limit: 2g
volumes:
- redis_data:/data
networks:
- backend
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
nats:
image: nats:2-alpine
command: ['-js', '-sd', '/data', '-m', '8222']
volumes:
- nats_data:/data
networks:
- backend
healthcheck:
test: ['CMD', 'wget', '--quiet', '--spider', 'http://127.0.0.1:8222/healthz']
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
nats-tools:
image: natsio/nats-box:latest
profiles: ['tools']
entrypoint: ['sleep', 'infinity']
depends_on:
nats:
condition: service_healthy
networks:
- backend
artifact-storage-init:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
user: '0:0'
entrypoint: ['sh', '-c']
command:
- mkdir -p /var/lib/huddle/artifacts /var/lib/huddle/artifact-staging && chown -R 10001:10001 /var/lib/huddle/artifacts /var/lib/huddle/artifact-staging && chmod 700 /var/lib/huddle/artifact-staging
volumes:
- artifact_data:/var/lib/huddle/artifacts
- artifact_staging:/var/lib/huddle/artifact-staging
restart: 'no'
# Memory limits below are ceilings against a runaway container, not allocations.
# CPU is deliberately left uncapped. Limits are literals because `docker compose
# publish` cannot interpolate numeric fields; override them with a compose file in
# COMPOSE_FILE (docs/production-deployment.md, "Change container resource limits").
api:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
mem_limit: 4g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_STARTUP_TIMEOUT: ${DATABASE_STARTUP_TIMEOUT:-120}
# Allow Zitadel bootstrap to render the stack before the org exists.
# The ownership migration rejects a missing ID before changing the schema.
ZITADEL_BOOTSTRAP_OWNER_ID: ${ZITADEL_BOOTSTRAP_OWNER_ID:-}
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
ARTIFACT_BLOB_ROOT: /var/lib/huddle/artifacts
ARTIFACT_STAGING_ROOT: /var/lib/huddle/artifact-staging
ARTIFACT_MAX_UPLOAD_BYTES: ${ARTIFACT_MAX_UPLOAD_BYTES:-26214400}
ARTIFACT_MAX_FILES_PER_UPLOAD: ${ARTIFACT_MAX_FILES_PER_UPLOAD:-20}
ARTIFACT_AGENT_WRITE_MAX_CHARS: ${ARTIFACT_AGENT_WRITE_MAX_CHARS:-100000}
ARTIFACT_TEXT_MAX_BYTES: ${ARTIFACT_TEXT_MAX_BYTES:-2097152}
ARTIFACT_VISION_MAX_EDGE_PX: ${ARTIFACT_VISION_MAX_EDGE_PX:-2048}
ARTIFACT_SVG_MAX_BYTES: ${ARTIFACT_SVG_MAX_BYTES:-5242880}
ARTIFACT_HTML_MAX_BYTES: ${ARTIFACT_HTML_MAX_BYTES:-5242880}
ARTIFACT_HTML_MAX_IMAGES: ${ARTIFACT_HTML_MAX_IMAGES:-10}
ARTIFACT_HTML_MIN_IMAGE_DIMENSION_PX: ${ARTIFACT_HTML_MIN_IMAGE_DIMENSION_PX:-32}
# Public HTTPS fetches (get_web_page). The byte limit follows the upload limit unless set.
WEB_PAGE_MAX_BYTES: ${WEB_PAGE_MAX_BYTES:-${ARTIFACT_MAX_UPLOAD_BYTES:-26214400}}
WEB_PAGE_CONNECT_TIMEOUT_SECONDS: ${WEB_PAGE_CONNECT_TIMEOUT_SECONDS:-5}
WEB_PAGE_TIMEOUT_SECONDS: ${WEB_PAGE_TIMEOUT_SECONDS:-60}
WEB_PAGE_MAX_REDIRECTS: ${WEB_PAGE_MAX_REDIRECTS:-5}
WEB_PAGE_USER_AGENT: ${WEB_PAGE_USER_AGENT:-Huddle/0.1.0 (+https://huddlehub.ai)}
WEB_PAGE_MAX_TEXT_BYTES: ${WEB_PAGE_MAX_TEXT_BYTES:-${ARTIFACT_TEXT_MAX_BYTES:-2097152}}
WEB_PAGE_IMAGE_MAX_BYTES: ${WEB_PAGE_IMAGE_MAX_BYTES:-8388608}
WEB_PAGE_RENDER_ENABLED: ${WEB_PAGE_RENDER_ENABLED:-true}
WEB_PAGE_RENDER_WS_URL: ${WEB_PAGE_RENDER_WS_URL-ws://web-renderer:3000/}
WEB_PAGE_RENDER_TIMEOUT_SECONDS: ${WEB_PAGE_RENDER_TIMEOUT_SECONDS:-30}
WEB_PAGE_RENDER_SETTLE_SECONDS: ${WEB_PAGE_RENDER_SETTLE_SECONDS:-5}
WEB_PAGE_RENDER_MAX_CONCURRENCY: ${WEB_PAGE_RENDER_MAX_CONCURRENCY:-4}
WEB_PAGE_RENDER_MAX_REQUESTS: ${WEB_PAGE_RENDER_MAX_REQUESTS:-150}
WEB_PAGE_RENDER_MAX_RESOURCE_BYTES: ${WEB_PAGE_RENDER_MAX_RESOURCE_BYTES:-5242880}
WEB_PAGE_RENDER_MAX_TOTAL_BYTES: ${WEB_PAGE_RENDER_MAX_TOTAL_BYTES:-26214400}
WEB_PAGE_VIEW_MAX_SEGMENTS: ${WEB_PAGE_VIEW_MAX_SEGMENTS:-3}
WEB_PAGE_VIEW_CACHE_TTL_SECONDS: ${WEB_PAGE_VIEW_CACHE_TTL_SECONDS:-3600}
WEB_PAGE_VIEW_MAX_OUTPUT_TOKENS: ${WEB_PAGE_VIEW_MAX_OUTPUT_TOKENS:-2000}
HTTP_TIMEOUT: ${HTTP_TIMEOUT:-300}
HTTPX_MAX_CONNECTIONS: ${HTTPX_MAX_CONNECTIONS:-100}
HTTP_MAX_KEEPALIVE: ${HTTP_MAX_KEEPALIVE:-20}
ASR_WS_URL: ${ASR_WS_URL:-ws://spark-427e.local:8005/v1/audio/transcriptions/realtime}
VLLM_API_KEY: ${VLLM_API_KEY:-EMPTY}
CHAT_HEIC_MODE: ${CHAT_HEIC_MODE:-passthrough}
CHAT_CONTEXT_USAGE_TIMEOUT_SECONDS: ${CHAT_CONTEXT_USAGE_TIMEOUT_SECONDS:-2.0}
EXA_API_KEY: ${EXA_API_KEY:-}
GOOGLE_PLACES_API_KEY: ${GOOGLE_PLACES_API_KEY:-}
REDIS_HOST: redis:6379
REDIS_KEY_PREFIX: ${REDIS_KEY_PREFIX:-huddle:}
REDIS_MAX_CONNECTIONS: ${REDIS_MAX_CONNECTIONS:-10}
REDIS_SOCKET_CONNECT_TIMEOUT: ${REDIS_SOCKET_CONNECT_TIMEOUT:-10.0}
REDIS_SOCKET_TIMEOUT: ${REDIS_SOCKET_TIMEOUT:-30.0}
REDIS_SOCKET_KEEPALIVE: ${REDIS_SOCKET_KEEPALIVE:-true}
REDIS_SSL_ENABLED: 'false'
REDIS_IS_CLUSTER: 'false'
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_HOSTS: ${OPS_HOSTS:-[]}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
WEB_PUSH_ENABLED: ${WEB_PUSH_ENABLED:-false}
WEB_PUSH_VAPID_PUBLIC_KEY: ${WEB_PUSH_VAPID_PUBLIC_KEY:-}
OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID:-}
OAUTH_ISSUER: ${OAUTH_ISSUER:-${ZITADEL_PUBLIC_SCHEME:-http}://${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}}
OAUTH_AUDIENCE: ${OAUTH_AUDIENCE:-}
OAUTH_JWKS_URL: ${OAUTH_JWKS_URL:-http://zitadel:8080/oauth/v2/keys}
OAUTH_JWKS_HOST_HEADER: ${OAUTH_JWKS_HOST_HEADER:-${ZITADEL_DOMAIN:-localhost}:${ZITADEL_EXTERNALPORT:-${ZITADEL_HTTP_PORT:-8080}}}
OAUTH_OWNER_CLAIM: ${OAUTH_OWNER_CLAIM:-urn:zitadel:iam:user:resourceowner:id}
OAUTH_END_SESSION_ENDPOINT: ${OAUTH_END_SESSION_ENDPOINT:-}
OAUTH_SCOPES: ${OAUTH_SCOPES:-}
ZITADEL_ADMIN_ORG_ID: ${ZITADEL_ADMIN_ORG_ID:-}
HUDDLE_PUBLIC_ORIGIN: ${HUDDLE_PUBLIC_ORIGIN:-https://localhost}
HUDDLE_LANDING_ORIGIN: ${HUDDLE_LANDING_ORIGIN:-}
INTEREST_TOKEN_SECRET: ${INTEREST_TOKEN_SECRET:-}
INTEREST_HOURLY_SIGNUP_LIMIT: ${INTEREST_HOURLY_SIGNUP_LIMIT:-60}
INTEREST_DAILY_EMAIL_LIMIT: ${INTEREST_DAILY_EMAIL_LIMIT:-100}
SMTP_HOST: ${SMTP_HOST:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USERNAME: ${SMTP_USERNAME:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
SMTP_FROM: ${SMTP_FROM:-}
SMTP_STARTTLS: ${SMTP_STARTTLS:-true}
EBAY_ENVIRONMENT: ${EBAY_ENVIRONMENT:-sandbox}
EBAY_CLIENT_ID: ${EBAY_CLIENT_ID:-}
EBAY_CLIENT_SECRET: ${EBAY_CLIENT_SECRET:-}
EBAY_REDIRECT_URI: ${EBAY_REDIRECT_URI:-}
EBAY_ALLOW_PRODUCTION: ${EBAY_ALLOW_PRODUCTION:-false}
EBAY_ACCOUNT_DELETION_VERIFICATION_TOKEN: ${EBAY_ACCOUNT_DELETION_VERIFICATION_TOKEN:-}
EBAY_ACCOUNT_DELETION_PUBLIC_ORIGIN: ${EBAY_ACCOUNT_DELETION_PUBLIC_ORIGIN:-}
EBAY_SANDBOX_API_URL: ${EBAY_SANDBOX_API_URL:-https://api.sandbox.ebay.com}
EBAY_PRODUCTION_API_URL: ${EBAY_PRODUCTION_API_URL:-https://api.ebay.com}
EBAY_SANDBOX_INVENTORY_API_URL: ${EBAY_SANDBOX_INVENTORY_API_URL:-https://api.sandbox.ebay.com/sell/inventory/v1}
EBAY_PRODUCTION_INVENTORY_API_URL: ${EBAY_PRODUCTION_INVENTORY_API_URL:-https://api.ebay.com/sell/inventory/v1}
depends_on:
artifact-storage-init:
condition: service_completed_successfully
postgres:
condition: service_healthy
redis:
condition: service_healthy
nats:
condition: service_healthy
volumes:
- artifact_data:/var/lib/huddle/artifacts
- artifact_staging:/var/lib/huddle/artifact-staging
- hf_cache:/var/lib/huddle/hf-cache
networks:
- renderer
- backend
- frontend
- webhook_backend
healthcheck:
test:
[
'CMD',
'python',
'-c',
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:9000/api/health', timeout=3)",
]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
restart: unless-stopped
ops-collector:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.ops_collector']
mem_limit: 1g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_COLLECTOR_ENABLED: ${OPS_COLLECTOR_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_SCRAPE_SECONDS: ${OPS_SCRAPE_SECONDS:-10}
OPS_HOST_SAMPLE_SECONDS: ${OPS_HOST_SAMPLE_SECONDS:-5}
OPS_HOSTS: ${OPS_HOSTS:-[]}
# The speech readiness check uses this address, so it must match the API's.
ASR_WS_URL: ${ASR_WS_URL:-ws://spark-427e.local:8005/v1/audio/transcriptions/realtime}
ARTIFACT_BLOB_ROOT: /var/lib/huddle/artifacts
volumes:
- artifact_data:/var/lib/huddle/artifacts:ro
depends_on:
postgres:
condition: service_healthy
networks:
- backend
- metrics_egress
restart: unless-stopped
schedule-publisher:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.scheduled_worker', 'scheduler']
mem_limit: 1g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
HUDDLE_PUBLIC_ORIGIN: ${HUDDLE_PUBLIC_ORIGIN:-https://localhost}
REDIS_HOST: redis:6379
REDIS_KEY_PREFIX: ${REDIS_KEY_PREFIX:-huddle:}
VLLM_API_KEY: ${VLLM_API_KEY:-EMPTY}
EXA_API_KEY: ${EXA_API_KEY:-}
GOOGLE_PLACES_API_KEY: ${GOOGLE_PLACES_API_KEY:-}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
api:
condition: service_healthy
networks:
- backend
restart: unless-stopped
notification-live-worker:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.live_outbox_worker', 'notifications']
mem_limit: 1g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
WEB_PUSH_VAPID_PUBLIC_KEY: ${WEB_PUSH_VAPID_PUBLIC_KEY:-}
WEB_PUSH_VAPID_PRIVATE_KEY: ${WEB_PUSH_VAPID_PRIVATE_KEY:-}
WEB_PUSH_VAPID_SUBJECT: ${WEB_PUSH_VAPID_SUBJECT:-}
WEB_PUSH_ENABLED: ${WEB_PUSH_ENABLED:-false}
HUDDLE_PUBLIC_ORIGIN: ${HUDDLE_PUBLIC_ORIGIN:-https://localhost}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
api:
condition: service_healthy
networks:
- backend
# Web Push must reach external browser push providers.
- frontend
restart: unless-stopped
schedule-live-worker:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.live_outbox_worker', 'schedules']
mem_limit: 1g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
api:
condition: service_healthy
networks:
- backend
restart: unless-stopped
chat-thread-live-worker:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.live_outbox_worker', 'chat.threads']
mem_limit: 1g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
api:
condition: service_healthy
networks:
- backend
restart: unless-stopped
scheduled-task-worker:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.scheduled_worker', 'worker']
mem_limit: 2g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
ARTIFACT_BLOB_ROOT: /var/lib/huddle/artifacts
ARTIFACT_VISION_MAX_EDGE_PX: ${ARTIFACT_VISION_MAX_EDGE_PX:-2048}
ARTIFACT_SVG_MAX_BYTES: ${ARTIFACT_SVG_MAX_BYTES:-5242880}
ARTIFACT_HTML_MAX_BYTES: ${ARTIFACT_HTML_MAX_BYTES:-5242880}
ARTIFACT_HTML_MAX_IMAGES: ${ARTIFACT_HTML_MAX_IMAGES:-10}
ARTIFACT_HTML_MIN_IMAGE_DIMENSION_PX: ${ARTIFACT_HTML_MIN_IMAGE_DIMENSION_PX:-32}
# Public HTTPS fetches (get_web_page). The byte limit follows the upload limit unless set.
WEB_PAGE_MAX_BYTES: ${WEB_PAGE_MAX_BYTES:-${ARTIFACT_MAX_UPLOAD_BYTES:-26214400}}
WEB_PAGE_CONNECT_TIMEOUT_SECONDS: ${WEB_PAGE_CONNECT_TIMEOUT_SECONDS:-5}
WEB_PAGE_TIMEOUT_SECONDS: ${WEB_PAGE_TIMEOUT_SECONDS:-60}
WEB_PAGE_MAX_REDIRECTS: ${WEB_PAGE_MAX_REDIRECTS:-5}
WEB_PAGE_USER_AGENT: ${WEB_PAGE_USER_AGENT:-Huddle/0.1.0 (+https://huddlehub.ai)}
WEB_PAGE_MAX_TEXT_BYTES: ${WEB_PAGE_MAX_TEXT_BYTES:-${ARTIFACT_TEXT_MAX_BYTES:-2097152}}
WEB_PAGE_IMAGE_MAX_BYTES: ${WEB_PAGE_IMAGE_MAX_BYTES:-8388608}
WEB_PAGE_RENDER_ENABLED: ${WEB_PAGE_RENDER_ENABLED:-true}
WEB_PAGE_RENDER_WS_URL: ${WEB_PAGE_RENDER_WS_URL-ws://web-renderer:3000/}
WEB_PAGE_RENDER_TIMEOUT_SECONDS: ${WEB_PAGE_RENDER_TIMEOUT_SECONDS:-30}
WEB_PAGE_RENDER_SETTLE_SECONDS: ${WEB_PAGE_RENDER_SETTLE_SECONDS:-5}
WEB_PAGE_RENDER_MAX_CONCURRENCY: ${WEB_PAGE_RENDER_MAX_CONCURRENCY:-4}
WEB_PAGE_RENDER_MAX_REQUESTS: ${WEB_PAGE_RENDER_MAX_REQUESTS:-150}
WEB_PAGE_RENDER_MAX_RESOURCE_BYTES: ${WEB_PAGE_RENDER_MAX_RESOURCE_BYTES:-5242880}
WEB_PAGE_RENDER_MAX_TOTAL_BYTES: ${WEB_PAGE_RENDER_MAX_TOTAL_BYTES:-26214400}
WEB_PAGE_VIEW_MAX_SEGMENTS: ${WEB_PAGE_VIEW_MAX_SEGMENTS:-3}
WEB_PAGE_VIEW_CACHE_TTL_SECONDS: ${WEB_PAGE_VIEW_CACHE_TTL_SECONDS:-3600}
WEB_PAGE_VIEW_MAX_OUTPUT_TOKENS: ${WEB_PAGE_VIEW_MAX_OUTPUT_TOKENS:-2000}
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
HUDDLE_PUBLIC_ORIGIN: ${HUDDLE_PUBLIC_ORIGIN:-https://localhost}
REDIS_HOST: redis:6379
REDIS_KEY_PREFIX: ${REDIS_KEY_PREFIX:-huddle:}
VLLM_API_KEY: ${VLLM_API_KEY:-EMPTY}
HTTP_TIMEOUT: ${HTTP_TIMEOUT:-300}
HTTPX_MAX_CONNECTIONS: ${HTTPX_MAX_CONNECTIONS:-100}
HTTP_MAX_KEEPALIVE: ${HTTP_MAX_KEEPALIVE:-20}
CHAT_HEIC_MODE: ${CHAT_HEIC_MODE:-passthrough}
CHAT_CONTEXT_USAGE_TIMEOUT_SECONDS: ${CHAT_CONTEXT_USAGE_TIMEOUT_SECONDS:-2.0}
EXA_API_KEY: ${EXA_API_KEY:-}
GOOGLE_PLACES_API_KEY: ${GOOGLE_PLACES_API_KEY:-}
EBAY_ENVIRONMENT: ${EBAY_ENVIRONMENT:-sandbox}
EBAY_CLIENT_ID: ${EBAY_CLIENT_ID:-}
EBAY_CLIENT_SECRET: ${EBAY_CLIENT_SECRET:-}
EBAY_REDIRECT_URI: ${EBAY_REDIRECT_URI:-}
EBAY_ALLOW_PRODUCTION: ${EBAY_ALLOW_PRODUCTION:-false}
EBAY_SANDBOX_API_URL: ${EBAY_SANDBOX_API_URL:-https://api.sandbox.ebay.com}
EBAY_PRODUCTION_API_URL: ${EBAY_PRODUCTION_API_URL:-https://api.ebay.com}
EBAY_SANDBOX_INVENTORY_API_URL: ${EBAY_SANDBOX_INVENTORY_API_URL:-https://api.sandbox.ebay.com/sell/inventory/v1}
EBAY_PRODUCTION_INVENTORY_API_URL: ${EBAY_PRODUCTION_INVENTORY_API_URL:-https://api.ebay.com/sell/inventory/v1}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
api:
condition: service_healthy
volumes:
- artifact_data:/var/lib/huddle/artifacts
networks:
- renderer
- backend
- frontend
- webhook_backend
restart: unless-stopped
artifact-index-worker:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-api:${HUDDLE_IMAGE_TAG:-dev}
entrypoint: ['python', '-m', 'huddle_api.artifact_index_worker']
mem_limit: 2g
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
DATABASE_HOST_OVERRIDE: postgres
DATABASE_PORT_OVERRIDE: '5432'
FERNET_KEY: ${FERNET_KEY:?FERNET_KEY is required}
ARTIFACT_BLOB_ROOT: /var/lib/huddle/artifacts
ARTIFACT_VISION_MAX_EDGE_PX: ${ARTIFACT_VISION_MAX_EDGE_PX:-2048}
ARTIFACT_SVG_MAX_BYTES: ${ARTIFACT_SVG_MAX_BYTES:-5242880}
ARTIFACT_HTML_MAX_BYTES: ${ARTIFACT_HTML_MAX_BYTES:-5242880}
ARTIFACT_HTML_MAX_IMAGES: ${ARTIFACT_HTML_MAX_IMAGES:-10}
ARTIFACT_HTML_MIN_IMAGE_DIMENSION_PX: ${ARTIFACT_HTML_MIN_IMAGE_DIMENSION_PX:-32}
NATS_URL: nats://nats:4222
LOG_LEVEL: ${LOG_LEVEL:-INFO}
OPS_TELEMETRY_ENABLED: ${OPS_TELEMETRY_ENABLED:-true}
OPS_FLUSH_SECONDS: ${OPS_FLUSH_SECONDS:-10}
OPS_ROLLUP_RETENTION_DAYS: ${OPS_ROLLUP_RETENTION_DAYS:-14}
OPS_EVENT_RETENTION_DAYS: ${OPS_EVENT_RETENTION_DAYS:-14}
OPS_TRACE_RETENTION_DAYS: ${OPS_TRACE_RETENTION_DAYS:-30}
HUDDLE_PUBLIC_ORIGIN: ${HUDDLE_PUBLIC_ORIGIN:-https://localhost}
REDIS_HOST: redis:6379
REDIS_KEY_PREFIX: ${REDIS_KEY_PREFIX:-huddle:}
VLLM_API_KEY: ${VLLM_API_KEY:-EMPTY}
HTTP_TIMEOUT: ${HTTP_TIMEOUT:-300}
HTTPX_MAX_CONNECTIONS: ${HTTPX_MAX_CONNECTIONS:-100}
HTTP_MAX_KEEPALIVE: ${HTTP_MAX_KEEPALIVE:-20}
CHAT_HEIC_MODE: ${CHAT_HEIC_MODE:-passthrough}
CHAT_CONTEXT_USAGE_TIMEOUT_SECONDS: ${CHAT_CONTEXT_USAGE_TIMEOUT_SECONDS:-2.0}
EXA_API_KEY: ${EXA_API_KEY:-}
GOOGLE_PLACES_API_KEY: ${GOOGLE_PLACES_API_KEY:-}
EBAY_ENVIRONMENT: ${EBAY_ENVIRONMENT:-sandbox}
EBAY_CLIENT_ID: ${EBAY_CLIENT_ID:-}
EBAY_CLIENT_SECRET: ${EBAY_CLIENT_SECRET:-}
EBAY_REDIRECT_URI: ${EBAY_REDIRECT_URI:-}
EBAY_ALLOW_PRODUCTION: ${EBAY_ALLOW_PRODUCTION:-false}
EBAY_SANDBOX_API_URL: ${EBAY_SANDBOX_API_URL:-https://api.sandbox.ebay.com}
EBAY_PRODUCTION_API_URL: ${EBAY_PRODUCTION_API_URL:-https://api.ebay.com}
EBAY_SANDBOX_INVENTORY_API_URL: ${EBAY_SANDBOX_INVENTORY_API_URL:-https://api.sandbox.ebay.com/sell/inventory/v1}
EBAY_PRODUCTION_INVENTORY_API_URL: ${EBAY_PRODUCTION_INVENTORY_API_URL:-https://api.ebay.com/sell/inventory/v1}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
api:
condition: service_healthy
volumes:
- artifact_data:/var/lib/huddle/artifacts
- hf_cache:/var/lib/huddle/hf-cache
networks:
- backend
- frontend
- webhook_backend
restart: unless-stopped
web:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-web:${HUDDLE_IMAGE_TAG:-dev}
networks:
- frontend
healthcheck:
test: ['CMD', 'wget', '--quiet', '--output-document=/dev/null', 'http://127.0.0.1/healthz']
interval: 10s
timeout: 5s
retries: 12
restart: unless-stopped
landing:
image: docker.io/${DOCKER_HUB_NAMESPACE:-local}/huddle-landing-web:${HUDDLE_IMAGE_TAG:-dev}
environment:
HUDDLE_APP_ORIGIN: ${HUDDLE_PUBLIC_ORIGIN:-https://localhost}
networks:
- frontend
healthcheck:
test: ['CMD', 'wget', '--quiet', '--output-document=/dev/null', 'http://127.0.0.1/healthz']
interval: 10s
timeout: 5s
retries: 12
restart: unless-stopped
caddy:
image: caddy:2-alpine
environment:
HUDDLE_HOSTNAME: ${HUDDLE_HOSTNAME:-localhost}
HUDDLE_LANDING_HOSTNAME: ${HUDDLE_LANDING_HOSTNAME:-landing.localhost}
HUDDLE_AUTH_HOSTNAME: ${HUDDLE_AUTH_HOSTNAME:-auth.localhost}
HUDDLE_TLS_ISSUER: ${HUDDLE_TLS_ISSUER:-internal}
configs:
- source: caddyfile
target: /etc/caddy/Caddyfile
ports:
- name: http
target: 80
published: ${HUDDLE_HTTP_PORT:-80}
protocol: tcp
- name: https
target: 443
published: ${HUDDLE_HTTPS_PORT:-443}
protocol: tcp
- name: http3
target: 443
published: ${HUDDLE_HTTPS_PORT:-443}
protocol: udp
volumes:
- caddy_data:/data
- caddy_config:/config
depends_on:
api:
condition: service_healthy
web:
condition: service_healthy
landing:
condition: service_healthy
networks:
- frontend
healthcheck:
test:
['CMD', 'wget', '--quiet', '--output-document=/dev/null', 'http://127.0.0.1:2019/config/']
interval: 10s
timeout: 5s
retries: 12
restart: unless-stopped
# The eBay webhook tunnel is for environments without a public domain (local
# and LAN staging). Start it with `--profile tunnel`; production does not need it.
ebay-webhook-gateway:
image: caddy:2-alpine
profiles: ['tunnel']
configs:
- source: ebay_webhook_caddyfile
target: /etc/caddy/Caddyfile
depends_on:
api:
condition: service_healthy
networks:
- webhook_edge
- webhook_backend
healthcheck:
test:
['CMD', 'wget', '--quiet', '--output-document=/dev/null', 'http://127.0.0.1:2019/config/']
interval: 10s
timeout: 5s
retries: 12
start_period: 5s
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared:latest
profiles: ['tunnel']
command: ['tunnel', '--no-autoupdate', '--url', 'http://ebay-webhook-gateway:8080']
depends_on:
ebay-webhook-gateway:
condition: service_healthy
networks:
- webhook_edge
restart: unless-stopped
configs:
zitadel_caddyfile:
file: ./zitadel.Caddyfile
caddyfile:
file: ./Caddyfile
ebay_webhook_caddyfile:
file: ./ebay-webhook.Caddyfile
networks:
metrics_egress:
renderer:
internal: true
frontend:
webhook_edge:
webhook_backend:
internal: true
backend:
internal: true
volumes:
zitadel_bootstrap:
postgres_data:
redis_data:
nats_data:
caddy_data:
caddy_config:
artifact_data:
artifact_staging:
hf_cache:
services:
api:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
artifact-index-worker:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
artifact-storage-init:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
caddy:
image: docker.io/library/caddy:2-alpine@sha256:eb4d35b7452d1ab6171fbdbe8d9275b2301dad6dad92115d91e19fb9b24d510f
chat-thread-live-worker:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
cloudflared:
image: docker.io/cloudflare/cloudflared:latest@sha256:1b1d34d0a9c0bfca9de72db8460d328db3f19edd2b96169114e3e893a97bbe53
ebay-webhook-gateway:
image: docker.io/library/caddy:2-alpine@sha256:eb4d35b7452d1ab6171fbdbe8d9275b2301dad6dad92115d91e19fb9b24d510f
landing:
image: docker.io/harlenalvarez/huddle-landing-web:2026.10.27-2@sha256:3bcaddefae31a5815aa40f7bb25b1ca9f7bb65e98e5f3947b4d7a2ea306e140c
nats:
image: docker.io/library/nats:2-alpine@sha256:823e7f1d6d8ffe3b30dbd73d4a06bbb939acf94929f7fdf224404da7a05bafb5
nats-tools:
image: docker.io/natsio/nats-box:latest@sha256:dd57687c32199867bfc42823e935174f37754a4281de957d6717469948b5b18b
notification-live-worker:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
ops-collector:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
postgres:
image: docker.io/harlenalvarez/huddle-postgres:2026.10.27-2@sha256:6edda3c7f9ba52fb2a7cb71b28cf99e701d4e97a02bf2e619282560a4de76940
redis:
image: docker.io/library/redis:8-alpine@sha256:47b08295737773de3b711fb86f13ae802f5074e96a9a2d29f5f80164ac08c046
schedule-live-worker:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
schedule-publisher:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
scheduled-task-worker:
image: docker.io/harlenalvarez/huddle-api:2026.10.27-2@sha256:e93608856482391a1b464990585cea76258fa41529f53f8a72b5fd76ec069af1
web:
image: docker.io/harlenalvarez/huddle-web:2026.10.27-2@sha256:d6161f9df8f3f51f7e1230ae91d7d4a96f8d03f208d88eff10b7b13c3e5220b6
web-renderer:
image: docker.io/harlenalvarez/huddle-web-renderer:2026.10.27-2@sha256:248a6d64c3f2680731ef2ee298c7739e599c0a017bb01206e3801aee13e26a42
zitadel:
image: docker.io/library/caddy:2-alpine@sha256:eb4d35b7452d1ab6171fbdbe8d9275b2301dad6dad92115d91e19fb9b24d510f
zitadel-api:
image: ghcr.io/zitadel/zitadel:v4.19.3@sha256:d8430210beea5319bce553a0e6ff77167e30633535c3e072938c6b675f3c115e
zitadel-db-init:
image: docker.io/harlenalvarez/huddle-postgres:2026.10.27-2@sha256:6edda3c7f9ba52fb2a7cb71b28cf99e701d4e97a02bf2e619282560a4de76940
zitadel-login:
image: ghcr.io/zitadel/zitadel-login:v4.19.3@sha256:f4fb0eb8acb474e8abba0698dfb69b8b67e0856f5cda9e787c2c67b8205976f3
docker compose -f oci://harlenalvarez/huddle-stack:latest upUse the above command to pull and run the Compose file. Learn more.
Redis is the world’s fastest data platform for caching, vector search, and NoSQL databases.
Pulls
1B+
Stars
13630
Last Updated
about 13 hours
Caddy 2 is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go.
Pulls
500M+
Stars
993
Last Updated
about 16 hours
NATS is an open-source, high-performance, cloud native messaging system.
Pulls
100M+
Stars
522
Last Updated
about 17 hours
Client for Cloudflare Tunnel, a daemon that exposes private services through the Cloudflare edge.
Pulls
100M+
Stars
593
Last Updated
1 day
Lightweight container with NATS and NATS Streaming utilities
Pulls
50M+
Stars
5
Last Updated
7 days