services: server: image: docker.io/awinterstein/habitica-server:latest restart: unless-stopped depends_on: mongo: condition: service_healthy environment: NODE_DB_URI: "mongodb://${MONGO_HABITICA_USER}:${MONGO_HABITICA_PASSWORD}@mongo/habitica?authSource=habitica" INVITE_ONLY: "${INVITE_ONLY}" EMAIL_SERVER_URL: "${EMAIL_SERVER_URL}" EMAIL_SERVER_PORT: "${EMAIL_SERVER_PORT}" EMAIL_SERVER_AUTH_USER: "${EMAIL_SERVER_AUTH_USER}" EMAIL_SERVER_AUTH_PASSWORD: "${EMAIL_SERVER_AUTH_PASSWORD}" ADMIN_EMAIL: "${ADMIN_EMAIL}" client: image: docker.io/awinterstein/habitica-client:latest restart: unless-stopped depends_on: - server ports: - "80" mongo: image: docker.io/mongo:latest restart: unless-stopped command: > bash -c " echo \"${MONGO_KEYFILE_CONTENT}\" > /etc/mongo-keyfile && chown 999:999 /etc/mongo-keyfile && chmod 400 /etc/mongo-keyfile && exec docker-entrypoint.sh mongod --replSet rs --bind_ip_all --port 27017 --keyFile /etc/mongo-keyfile " environment: MONGO_INITDB_ROOT_USERNAME: ${MONGO_ADMIN_USER} MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ADMIN_PASSWORD} MONGO_KEYFILE_CONTENT: ${MONGO_KEYFILE_CONTENT} MONGO_HABITICA_USER: ${MONGO_HABITICA_USER} MONGO_HABITICA_PASSWORD: ${MONGO_HABITICA_PASSWORD} # --------------------------------------------------------- # SMART HEALTHCHECK: Auto-fixes Hostname Mismatches for replicaSet # --------------------------------------------------------- healthcheck: test: | mongosh --port 27017 --quiet -u "${MONGO_ADMIN_USER}" -p "${MONGO_ADMIN_PASSWORD}" --authenticationDatabase admin --eval " try { // 1. Hostname Fix const config = rs.conf(); const currentHost = require('os').hostname() + ':27017'; if (config.members[0].host !== currentHost) { config.members[0].host = currentHost; rs.reconfig(config, { force: true }); } // 2. User Creation Logic const targetDb = db.getSiblingDB('habitica'); const hUser = process.env.MONGO_HABITICA_USER; const hPass = process.env.MONGO_HABITICA_PASSWORD; // We can only check/create users if we are Primary if (rs.isMaster().ismaster) { if (!targetDb.getUser(hUser)) { print('Creating missing user ' + hUser + '...'); targetDb.createUser({ user: hUser, pwd: hPass, roles: ['readWrite'] }); } // SUCCESS: User exists and we are Primary quit(0); } else { // We are not Primary yet (still electing), so we cannot confirm user exists. // Fail the check so the dependent app waits. print('Waiting for Primary state...'); quit(1); } } catch (err) { // If not initialized, initiate and FAIL this check so we wait for the next cycle try { rs.initiate({ _id: 'rs', members: [{ _id: 0, host: require('os').hostname() + ':27017' }] }); } catch (e) {} quit(1); } " interval: 5s timeout: 10s retries: 20 volumes: - habitica-mongo-data:/data/db volumes: habitica-mongo-data: {}