AiToEarn Docker & Self-Hosted Deployment

GitHub

TypeScript auto-publish & douyin repository by @yikart. Model Context Protocol (MCP) server & tools with 24k stars.

RAW Compose
1. Pull Image (DOCKERHUB) docker pull aitoearn/aitoearn-ai
2. Docker Run (1-Click Launch) docker run -d --name aitoearn -p 27017:27017 --restart always aitoearn/aitoearn-ai:latest
3. Podman Run podman run -d --name aitoearn -p 27017:27017 --restart always aitoearn/aitoearn-ai:latest
4. Skopeo Inspect (Zero-Pull Manifest) skopeo inspect docker://docker.io/aitoearn/aitoearn-ai:latest
5. ORAS Pull (OCI Artifact) oras pull aitoearn/aitoearn-ai:latest
6. Compose Deployment docker compose up -d
docker-compose.yml
Download .yml
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