services: mongodb: image: mongo:latest container_name: aitoearn-mongodb restart: unless-stopped entrypoint: ["bash", "-c", "openssl rand -base64 756 > /data/mongodb-keyfile && chmod 400 /data/mongodb-keyfile && chown mongodb:mongodb /data/mongodb-keyfile && exec docker-entrypoint.sh mongod --replSet rs0 --keyFile /data/mongodb-keyfile"] environment: MONGO_INITDB_ROOT_USERNAME: admin MONGO_INITDB_ROOT_PASSWORD: password ports: - "27017:27017" volumes: - mongodb-data:/data/db - mongodb-config:/data/configdb networks: - aitoearn-network healthcheck: test: ["CMD-SHELL", "mongosh mongodb://admin:password@localhost:27017/?authSource=admin --quiet --eval \"try { quit(rs.status().ok === 1 ? 0 : 1) } catch(e) { quit(1) }\""] interval: 10s timeout: 5s retries: 10 start_period: 40s mongodb-rs-init: image: mongo:latest container_name: aitoearn-mongodb-rs-init depends_on: mongodb: condition: service_started entrypoint: ["/bin/sh", "-c", "until mongosh mongodb://admin:password@mongodb:27017/?authSource=admin --quiet --eval \"db.adminCommand('ping').ok\" >/dev/null 2>&1; do sleep 2; done; mongosh mongodb://admin:password@mongodb:27017/?authSource=admin --eval \"try { rs.initiate({_id:'rs0', members:[{_id:0, host:'mongodb:27017'}]}) } catch(e) { if (e.codeName!=='AlreadyInitialized') throw e }\""] restart: "no" networks: - aitoearn-network redis: image: redis:latest container_name: aitoearn-redis restart: unless-stopped command: redis-server --requirepass password ports: - "6379:6379" volumes: - redis-data:/data networks: - aitoearn-network healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 10s timeout: 3s retries: 5 start_period: 30s rustfs: image: rustfs/rustfs:latest container_name: aitoearn-rustfs hostname: rustfs.local restart: unless-stopped environment: RUSTFS_ACCESS_KEY: rustfsadmin RUSTFS_SECRET_KEY: rustfsadmin ports: - "9001:9001" volumes: - rustfs-data:/data networks: aitoearn-network: aliases: - rustfs.local healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/health"] interval: 10s timeout: 5s retries: 5 start_period: 30s aitoearn-init: image: node:lts-alpine container_name: aitoearn-init restart: "no" depends_on: mongodb: condition: service_healthy volumes: - ./scripts/init.mjs:/app/init.mjs:ro - ./scripts/init-package.json:/app/package.json:ro - init-data:/data/init working_dir: /app environment: MONGO_URI: mongodb://admin:password@mongodb:27017 JWT_SECRET: change-this-jwt-secret DB_NAME: aitoearn AUTO_LOGIN_TOKEN_PATH: /data/init/token.txt entrypoint: /bin/sh -c "npm install --omit=dev 2>/dev/null && node init.mjs" networks: - aitoearn-network rustfs-init: image: minio/mc:latest container_name: aitoearn-rustfs-init depends_on: rustfs: condition: service_healthy networks: - aitoearn-network entrypoint: > /bin/sh -c " mc alias set rustfs http://rustfs.local:9000 rustfsadmin rustfsadmin; mc mb rustfs/aitoearn --ignore-existing; mc anonymous set download rustfs/aitoearn; exit 0; " aitoearn-ai: image: aitoearn/aitoearn-ai:latest pull_policy: always container_name: aitoearn-ai restart: unless-stopped depends_on: mongodb: condition: service_healthy redis: condition: service_healthy volumes: - ./project/aitoearn-backend/apps/aitoearn-ai/config/config.yaml:/app/config.yaml:rw networks: - aitoearn-network healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3010/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"] interval: 30s timeout: 10s retries: 3 start_period: 60s aitoearn-server: image: aitoearn/aitoearn-server:latest pull_policy: always container_name: aitoearn-server restart: unless-stopped depends_on: mongodb: condition: service_healthy redis: condition: service_healthy aitoearn-ai: condition: service_healthy volumes: - ./project/aitoearn-backend/apps/aitoearn-server/config/config.yaml:/app/config.yaml:rw networks: - aitoearn-network healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3002/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"] interval: 30s timeout: 10s retries: 3 start_period: 60s aitoearn-web: image: aitoearn/aitoearn-web:latest pull_policy: always container_name: aitoearn-web restart: unless-stopped depends_on: aitoearn-server: condition: service_healthy aitoearn-init: condition: service_completed_successfully volumes: - init-data:/data/init:ro networks: - aitoearn-network command: ["/bin/sh", "-c", "AUTO_LOGIN_TOKEN=$$(cat /data/init/token.txt 2>/dev/null || true); if [ -z \"$$AUTO_LOGIN_TOKEN\" ]; then echo 'AUTO_LOGIN_TOKEN is missing. Check the aitoearn-init container logs.' >&2; exit 1; fi; export AUTO_LOGIN_TOKEN; exec node server.js"] environment: NODE_ENV: production NEXT_TELEMETRY_DISABLED: 1 healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000', (r) => { process.exit(r.statusCode < 500 ? 0 : 1) }).on('error', () => process.exit(1))"] interval: 10s timeout: 5s retries: 6 start_period: 30s nginx: image: nginx:alpine container_name: aitoearn-nginx restart: unless-stopped depends_on: aitoearn-web: condition: service_healthy aitoearn-server: condition: service_healthy aitoearn-ai: condition: service_healthy ports: - "8080:80" - "9000:9000" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro networks: - aitoearn-network healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/_nhealth"] interval: 10s timeout: 5s retries: 3 start_period: 10s networks: aitoearn-network: driver: bridge name: aitoearn-network volumes: mongodb-data: driver: local mongodb-config: driver: local redis-data: driver: local rustfs-data: driver: local init-data: driver: local