services: traefik-certs: image: alpine:latest restart: "no" entrypoint: ["sh", "-c"] command: - | if [ ! -f /certs/cert.pem ]; then apk add --no-cache openssl >/dev/null 2>&1 openssl req -x509 -newkey rsa:4096 \ -keyout /certs/key.pem -out /certs/cert.pem \ -days 365 -nodes \ -subj "/CN=$$PUBLIC_HOST" \ -addext "subjectAltName=DNS:$$PUBLIC_HOST,DNS:*.$$PUBLIC_HOST" echo "Generated self-signed cert for $$PUBLIC_HOST" else echo "Certs already exist, skipping" fi environment: PUBLIC_HOST: ${PUBLIC_HOST:-localhost} volumes: - certs:/certs traefik: image: traefik:v3 restart: unless-stopped depends_on: traefik-certs: condition: service_completed_successfully ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik.yml:/etc/traefik/traefik.yml:ro - ./docker/traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro - acme:/etc/traefik/acme - certs:/etc/traefik/certs:ro networks: - cameleer postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-cameleer_saas} POSTGRES_USER: ${POSTGRES_USER:-cameleer} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev} volumes: - pgdata:/var/lib/postgresql/data - ./docker/init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-cameleer} -d ${POSTGRES_DB:-cameleer_saas}"] interval: 5s timeout: 5s retries: 5 networks: - cameleer logto: image: ghcr.io/logto-io/logto:latest restart: unless-stopped depends_on: postgres: condition: service_healthy entrypoint: ["sh", "-c", "npm run cli db seed -- --swe && npm start"] environment: DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD:-cameleer_dev}@postgres:5432/logto ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} ADMIN_ENDPOINT: http://${PUBLIC_HOST:-localhost}:3002 TRUST_PROXY_HEADER: 1 healthcheck: test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:3001/oidc/.well-known/openid-configuration', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))\""] interval: 5s timeout: 5s retries: 30 start_period: 15s labels: - traefik.enable=true - traefik.http.routers.logto.rule=PathPrefix(`/`) - traefik.http.routers.logto.priority=1 - traefik.http.routers.logto.entrypoints=websecure - traefik.http.routers.logto.tls=true - traefik.http.services.logto.loadbalancer.server.port=3001 networks: - cameleer logto-bootstrap: image: postgres:16-alpine depends_on: logto: condition: service_healthy cameleer3-server: condition: service_healthy restart: "no" entrypoint: ["sh", "/scripts/logto-bootstrap.sh"] environment: LOGTO_ENDPOINT: http://logto:3001 LOGTO_ADMIN_ENDPOINT: http://logto:3002 LOGTO_PUBLIC_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} PUBLIC_HOST: ${PUBLIC_HOST:-localhost} PUBLIC_PROTOCOL: ${PUBLIC_PROTOCOL:-https} PG_HOST: postgres PG_USER: ${POSTGRES_USER:-cameleer} PG_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev} PG_DB_SAAS: ${POSTGRES_DB:-cameleer_saas} SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin} SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:-admin} TENANT_ADMIN_USER: ${TENANT_ADMIN_USER:-camel} TENANT_ADMIN_PASS: ${TENANT_ADMIN_PASS:-camel} CAMELEER_AUTH_TOKEN: ${CAMELEER_AUTH_TOKEN:-default-bootstrap-token} SERVER_ENDPOINT: http://cameleer3-server:8081 SERVER_UI_USER: ${CAMELEER_UI_USER:-admin} SERVER_UI_PASS: ${CAMELEER_UI_PASSWORD:-admin} volumes: - ./docker/logto-bootstrap.sh:/scripts/logto-bootstrap.sh:ro - bootstrapdata:/data networks: - cameleer cameleer-saas: image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} restart: unless-stopped depends_on: postgres: condition: service_healthy logto-bootstrap: condition: service_completed_successfully volumes: - /var/run/docker.sock:/var/run/docker.sock - jardata:/data/jars - bootstrapdata:/data/bootstrap:ro environment: SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB:-cameleer_saas} SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev} LOGTO_ENDPOINT: ${LOGTO_ENDPOINT:-http://logto:3001} LOGTO_PUBLIC_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} LOGTO_ISSUER_URI: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}/oidc LOGTO_JWK_SET_URI: ${LOGTO_ENDPOINT:-http://logto:3001}/oidc/jwks LOGTO_M2M_CLIENT_ID: ${LOGTO_M2M_CLIENT_ID:-} LOGTO_M2M_CLIENT_SECRET: ${LOGTO_M2M_CLIENT_SECRET:-} CAMELEER3_SERVER_ENDPOINT: http://cameleer3-server:8081 CLICKHOUSE_URL: jdbc:clickhouse://clickhouse:8123/cameleer labels: - traefik.enable=true - traefik.http.routers.saas.rule=PathPrefix(`/platform`) - traefik.http.routers.saas.entrypoints=websecure - traefik.http.routers.saas.tls=true - traefik.http.services.saas.loadbalancer.server.port=8080 networks: - cameleer cameleer3-server: image: ${CAMELEER3_SERVER_IMAGE:-gitea.siegeln.net/cameleer/cameleer3-server}:${VERSION:-latest} restart: unless-stopped depends_on: postgres: condition: service_healthy clickhouse: condition: service_started environment: SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/cameleer3 SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev} CLICKHOUSE_URL: jdbc:clickhouse://clickhouse:8123/cameleer CAMELEER_AUTH_TOKEN: ${CAMELEER_AUTH_TOKEN:-default-bootstrap-token} CAMELEER_JWT_SECRET: ${CAMELEER_JWT_SECRET:-cameleer-dev-jwt-secret-change-in-production} CAMELEER_TENANT_ID: ${CAMELEER_TENANT_SLUG:-default} CAMELEER_OIDC_ISSUER_URI: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}/oidc CAMELEER_OIDC_JWK_SET_URI: ${LOGTO_ENDPOINT:-http://logto:3001}/oidc/jwks CAMELEER_OIDC_AUDIENCE: ${CAMELEER_OIDC_AUDIENCE:-https://api.cameleer.local} healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:8081/api/v1/health || exit 1"] interval: 5s timeout: 5s retries: 30 start_period: 15s labels: - traefik.enable=true - traefik.http.routers.observe.rule=PathPrefix(`/observe`) - traefik.http.routers.observe.service=observe - traefik.http.services.observe.loadbalancer.server.port=8080 - traefik.http.routers.dashboard.rule=PathPrefix(`/dashboard`) - traefik.http.routers.dashboard.service=dashboard - traefik.http.routers.dashboard.middlewares=dashboard-strip - traefik.http.middlewares.dashboard-strip.stripprefix.prefixes=/dashboard - traefik.http.services.dashboard.loadbalancer.server.port=8080 networks: - cameleer cameleer3-server-ui: image: ${CAMELEER3_SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer3-server-ui}:${VERSION:-latest} restart: unless-stopped entrypoint: ["/bin/sh", "/cameleer-entrypoint-patched.sh"] depends_on: cameleer3-server: condition: service_healthy volumes: - ./docker/server-ui-entrypoint.sh:/cameleer-entrypoint-patched.sh:ro environment: CAMELEER_API_URL: http://cameleer3-server:8081 BASE_PATH: /server labels: - traefik.enable=true - traefik.http.routers.server-ui.rule=PathPrefix(`/server`) - traefik.http.routers.server-ui.entrypoints=websecure - traefik.http.routers.server-ui.tls=true - traefik.http.routers.server-ui.service=server-ui - traefik.http.services.server-ui.loadbalancer.server.port=80 networks: - cameleer clickhouse: image: clickhouse/clickhouse-server:latest restart: unless-stopped volumes: - chdata:/var/lib/clickhouse - ./docker/clickhouse-init.sql:/docker-entrypoint-initdb.d/init.sql:ro - ./docker/clickhouse-users.xml:/etc/clickhouse-server/users.d/default-user.xml:ro healthcheck: test: ["CMD-SHELL", "clickhouse-client --query 'SELECT 1'"] interval: 10s timeout: 5s retries: 3 networks: - cameleer networks: cameleer: driver: bridge volumes: pgdata: chdata: acme: certs: jardata: bootstrapdata: