name: geolens x-app-env: &app-env GEOLENS_ENVIRONMENT: ${GEOLENS_ENVIRONMENT:-development} GEOLENS_LOG_LEVEL: ${GEOLENS_LOG_LEVEL:-INFO} GEOLENS_LOG_FORMAT: ${GEOLENS_LOG_FORMAT:-json} # Hostnames and the volume mount point are facts about this compose network, # not user configuration, so they are fixed here. Interpolating them from .env # would let a .env written for host-local development (localhost, ./var) leak # into the containers and break them. Credentials stay configurable, and are # shared with the postgres service below so the two cannot disagree. GEOLENS_DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-geolens}:${POSTGRES_PASSWORD:-geolens}@postgres:5432/${POSTGRES_DB:-geolens} GEOLENS_REDIS_URL: redis://redis:6379/0 GEOLENS_STORAGE_BACKEND: filesystem GEOLENS_STORAGE_ROOT: /data/objects GEOLENS_CRAWLER_USER_AGENT: ${GEOLENS_CRAWLER_USER_AGENT:-GeoLensBot/0.1 (+https://github.com/geolens/geolens)} GEOLENS_CRAWLER_RESPECT_ROBOTS: ${GEOLENS_CRAWLER_RESPECT_ROBOTS:-true} GEOLENS_CRAWLER_REQUESTS_PER_SECOND: ${GEOLENS_CRAWLER_REQUESTS_PER_SECOND:-1.0} GEOLENS_CRAWLER_CONCURRENCY_PER_HOST: ${GEOLENS_CRAWLER_CONCURRENCY_PER_HOST:-2} GEOLENS_CRAWLER_MAX_URLS: ${GEOLENS_CRAWLER_MAX_URLS:-500} GEOLENS_CRAWLER_MAX_DEPTH: ${GEOLENS_CRAWLER_MAX_DEPTH:-5} # The bundled fixture site lives on the compose network, which is a private # address range, and listens on 8081. Both opt-ins exist so the quick start # works out of the box; real deployments must remove them. GEOLENS_CRAWLER_ALLOW_PRIVATE_NETWORKS: ${GEOLENS_CRAWLER_ALLOW_PRIVATE_NETWORKS:-true} GEOLENS_CRAWLER_ALLOWED_PORTS: ${GEOLENS_CRAWLER_ALLOWED_PORTS:-[80,443,8081]} GEOLENS_AI_ENABLED: "false" GEOLENS_CLOUD_AI_ENABLED: "false" services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER:-geolens} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geolens} POSTGRES_DB: ${POSTGRES_DB:-geolens} volumes: - postgres-data:/var/lib/postgresql/data ports: # Published on a non-default port so `make up` does not collide with a # PostgreSQL already running on the host (common on a dev Mac via # Homebrew). Containers still talk to each other on 5432 internally. - "${POSTGRES_PORT:-55432}:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-geolens} -d ${POSTGRES_DB:-geolens}"] interval: 5s timeout: 5s retries: 20 redis: image: redis:7-alpine command: ["redis-server", "--appendonly", "no", "--save", ""] ports: # Non-default for the same reason as postgres above. - "${REDIS_PORT:-56379}:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 20 api: build: . # api, worker and fixture-site share one build context, so naming the image # explicitly makes Compose build it once instead of three times. image: geolens:local environment: *app-env command: > sh -c "alembic upgrade head && uvicorn apps.api.main:app --host 0.0.0.0 --port 8080" ports: - "${API_PORT:-8888}:8080" volumes: - object-data:/data/objects depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:8080/healthz"] interval: 10s timeout: 5s retries: 12 worker: build: . image: geolens:local environment: *app-env command: ["arq", "apps.worker.main.WorkerSettings"] volumes: - object-data:/data/objects depends_on: api: condition: service_healthy fixture-site: build: . image: geolens:local environment: GEOLENS_LOG_LEVEL: ${GEOLENS_LOG_LEVEL:-INFO} command: ["uvicorn", "apps.fixture_site.main:app", "--host", "0.0.0.0", "--port", "8081"] ports: - "${FIXTURE_SITE_PORT:-8081}:8081" volumes: postgres-data: object-data: