services: mediacrawler: build: . image: mediacrawler:latest container_name: mediacrawler restart: unless-stopped # Run as the user that owns this checkout. Without it the container is root, # and every file it writes into the mounted tree -- the crawler's per-run # jsonl output above all -- comes out root-owned. That does not break the app, # but it does lock the operator out of moving or deleting their own # deployment, which is exactly what happened the first time this was deployed. user: "1000:1000" # host networking is a requirement, not a convenience: the crawler attaches # to the operator's Chrome at 127.0.0.1:9222, and inside a bridge network # that loopback is the container's own, where no browser is listening. # It also puts the app port directly on the host, so `ports:` is not used. network_mode: host env_file: - .env environment: MC_HOST: 0.0.0.0 MC_PORT: "18051" TZ: Asia/Shanghai volumes: # The code is mounted rather than baked in, so shipping a change is # "git pull, restart" instead of an image rebuild. Only the dependencies # live in the image, because those are the expensive part and they change # rarely -- rebuild only when requirements.txt or the Dockerfile changes. - ./:/app