services: backend: build: context: ./backend dockerfile: Dockerfile container_name: plant-scraper-backend # Port exposed only internally, nginx proxies to it volumes: - ./backend:/app - ./data:/data environment: - DATABASE_URL=sqlite:////data/db/plants.sqlite - REDIS_URL=redis://redis:6379/0 - IMAGES_PATH=/data/images - EXPORTS_PATH=/data/exports - IMPORTS_PATH=/data/imports - LOGS_PATH=/data/logs depends_on: - redis command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload celery: build: context: ./backend dockerfile: Dockerfile container_name: plant-scraper-celery volumes: - ./backend:/app - ./data:/data environment: - DATABASE_URL=sqlite:////data/db/plants.sqlite - REDIS_URL=redis://redis:6379/0 - IMAGES_PATH=/data/images - EXPORTS_PATH=/data/exports - IMPORTS_PATH=/data/imports - LOGS_PATH=/data/logs depends_on: - redis command: celery -A app.workers.celery_app worker --beat --loglevel=info --concurrency=4 redis: image: redis:7-alpine container_name: plant-scraper-redis # Port exposed only internally, not to host (avoid conflicts) volumes: - redis_data:/data frontend: build: context: ./frontend dockerfile: Dockerfile container_name: plant-scraper-frontend # Port exposed only internally, nginx proxies to it volumes: - ./frontend:/app - /app/node_modules environment: - VITE_API_URL= command: npm run dev -- --host nginx: image: nginx:alpine container_name: plant-scraper-nginx ports: - "80:80" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - backend - frontend volumes: redis_data: